From 43016efc70d37cfb4bcce30f16ff5f42d026ad9a Mon Sep 17 00:00:00 2001 From: Luke Date: Thu, 20 Jun 2024 11:28:50 +0100 Subject: [PATCH] fix(TM-43864): Fixed delta formatting in new version of quill-editor --- .../pages/components/quill_editor_demo.dart | 243 +++++++++++++----- .../organisms/html_preview/html_body.dart | 50 +++- .../organisms/quill_editor/quill_delta.dart | 51 +++- pubspec.yaml | 2 +- 4 files changed, 269 insertions(+), 77 deletions(-) diff --git a/example/lib/pages/components/quill_editor_demo.dart b/example/lib/pages/components/quill_editor_demo.dart index 0a0d37a..d3f8334 100644 --- a/example/lib/pages/components/quill_editor_demo.dart +++ b/example/lib/pages/components/quill_editor_demo.dart @@ -15,85 +15,122 @@ class QuillEditorDemo extends StatefulWidget { class _QuillEditorDemoState extends State { final controller = QuillController.basic(); + var _htmlPreview = true; + + get htmlPreview => _htmlPreview; + + set htmlPreview(value) { + if (htmlPreview == value) return; + setState(() { + _htmlPreview = value; + }); + } + + var _loading = true; + + get loading => _loading; + + set loading(value) { + if (loading == value) return; + setState(() { + _loading = value; + }); + } + @override void initState() { super.initState(); - ZdsQuillDelta.fromHtml(''' -

Google Doodles




Doodles celebrate a wide range of events, from national holidays like Independence Day and Christmas to special occasions like the anniversary of the first moon landing or the birth of significant figures in history such as Albert Einstein and Frida Kahlo.

- ''').then((value) { + loading = true; + ZdsQuillDelta.fromHtml(editorData).then((value) { controller.document = value.document; + }).whenComplete(() { + loading = false; }); } @override Widget build(BuildContext context) { - return Scaffold( - appBar: AppBar( - title: const Text('Quill Editor'), - actions: [ - IconButton( - icon: const Icon(Icons.html), - onPressed: () { - final html = ZdsQuillDelta(document: controller.document).toHtml(); - showDialog( - context: context, - builder: (context) { - return ConstrainedBox( - constraints: BoxConstraints(maxHeight: MediaQuery.of(context).size.height * 0.7), - child: Dialog( - child: SingleChildScrollView( - child: Column( - children: [ - Text(html), - ], - ), - ))); - }, - ); - }, - ), - ], - ), - floatingActionButton: FloatingActionButton( - child: const Icon(Icons.edit), - onPressed: () { - ZdsQuillEditorPage.edit( - context, - title: 'Edit Notes', - embedBuilders: getEmbedBuilders(), - initialDelta: ZdsQuillDelta(document: controller.document), - charLimit: 20000, - embedButtons: FlutterQuillEmbeds.toolbarButtons( - videoButtonOptions: null, - imageButtonOptions: QuillToolbarImageButtonOptions( - imageButtonConfigurations: QuillToolbarImageConfigurations( - onImageInsertCallback: (image, controller) async { - // Upload to cloud - controller.insertImageBlock(imageSource: image); - }, - ), - ), + return Column( + children: [ + Expanded( + child: Scaffold( + backgroundColor: Zeta.of(context).colors.surfacePrimary, + appBar: AppBar( + title: const Text('Quill Editor'), + actions: [ + Row( + children: [ + Text('HTML'), + Switch( + value: htmlPreview, + onChanged: (value) { + setState(() { + htmlPreview = !htmlPreview; + }); + }, + ), + ], + ) + ], + ), + floatingActionButton: FloatingActionButton( + child: const Icon(Icons.edit), + onPressed: () { + ZdsQuillEditorPage.edit( + context, + title: 'Edit Notes', + embedBuilders: getEmbedBuilders(), + initialDelta: ZdsQuillDelta(document: controller.document), + charLimit: 20000, + embedButtons: FlutterQuillEmbeds.toolbarButtons( + videoButtonOptions: null, + imageButtonOptions: QuillToolbarImageButtonOptions( + imageButtonConfigurations: QuillToolbarImageConfigurations( + onImageInsertCallback: (image, controller) async { + // Upload to cloud + controller.insertImageBlock(imageSource: image); + }, + ), + ), + ), + ).then((value) { + if (value != null) { + setState(() { + controller.document = value.document; + }); + } + }); + }, ), - ).then((value) { - if (value != null) { - controller.document = value.document; - } - }); - }, - ), - body: Column( - children: [ - Expanded( - child: ZdsQuillEditor( - controller: controller, - readOnly: true, - padding: const EdgeInsets.all(16), - embedBuilders: getEmbedBuilders(), - focusNode: FocusNode(canRequestFocus: false), + body: Stack( + children: [ + ZdsQuillEditor( + controller: controller, + readOnly: true, + padding: const EdgeInsets.all(16), + embedBuilders: getEmbedBuilders(), + focusNode: FocusNode(canRequestFocus: false), + ), + if (loading) const LinearProgressIndicator(), + ], ), ), - ], - ), + ), + const Divider(), + Expanded( + child: Scaffold( + backgroundColor: Zeta.of(context).colors.surfacePrimary, + body: Builder(builder: (context) { + final html = ZdsQuillDelta(document: controller.document).toHtml(); + return SingleChildScrollView( + padding: EdgeInsets.all(14), + physics: ClampingScrollPhysics(), + child: htmlPreview ? ZdsHtmlContainer(html, expanded: true, showReadMore: false) : Text(html), + ); + }), + ), + ) + ], ); } } @@ -106,3 +143,75 @@ List getEmbedBuilders() { return FlutterQuillEmbeds.editorBuilders(); } } + +const editorData = """ +

+
    +
  • Apple
  • +
  • Banana
  • +
  • Cherry
  • +
+
+
    +
  1. Plan the trip
      +
    1. Book flights
    2. +
    +
  2. +
  3. Reserve hotel
      +
    1. Something
    2. +
    +
  4. +
  5. Check review
      +
    1. Read recent comments
    2. +
    +
  6. +
  7. Compare prices
      +
    1. Use comparison websites
    2. +
    +
  8. +
  9. Pack luggage
      +
    1. Clothes
    2. +
    +
  10. +
+
+
    +
  • Toiletries
      +
    • Shampoo
    • +
    • Toothbrush
    • +
    +
  • +
  • Gadgets
      +
    • Phone
    • +
    • Charger
    • +
    +
  • +
+
+
Code snippet example:
+Line 1: Initialize the project
+Line 2: Write some code
+Line 3: Test the code
+
+
“The only limit to our realization of tomorrow is our + doubts of today.”
– Franklin D. Roosevelt
+
+

Main Title

+
+

Subtitle

+
+

Section Title

+


Bold text example

Italic text example
Underlined text example

Strikethrough text + example

Small text example

Inline code example

Red colored text

Yellow + background text

Indented paragraph example. Lorem ipsum dolor sit amet, consectetur adipiscing + elit. Vivamus lacinia odio vitae vestibulum vestibulum.

Another indented paragraph. Cras placerat + ultricies orci nec vestibulum.

Left aligned text example

+

Right aligned text example

+
+

Center aligned text example

+
+

Right to left text example

+


Multi-line strikethrough example:
Line 1
Line 2
Line 3

+"""; diff --git a/lib/src/components/organisms/html_preview/html_body.dart b/lib/src/components/organisms/html_preview/html_body.dart index 95f6193..9e2c74c 100644 --- a/lib/src/components/organisms/html_preview/html_body.dart +++ b/lib/src/components/organisms/html_preview/html_body.dart @@ -107,24 +107,42 @@ class ZdsHtml extends StatelessWidget { } Map _buildStyles(BuildContext context, ColorScheme colorscheme, BoxConstraints box) { + final zetaColors = Zeta.of(context).colors; + final bodyMedium = Theme.of(context).textTheme.bodyMedium; + final fSize = fontSize ?? bodyMedium?.fontSize; return { 'p': Style( - fontSize: fontSize != null ? FontSize(fontSize!) : null, + fontSize: fSize != null ? FontSize(fSize) : null, maxLines: maxLines, before: '\n', lineHeight: LineHeight.percent(125), textOverflow: TextOverflow.ellipsis, - margin: Margins.only(left: 0, right: 0), + margin: Margins.zero, + padding: HtmlPaddings.zero, ), 'li': Style( fontSize: fontSize != null ? FontSize(fontSize!) : null, maxLines: maxLines, lineHeight: LineHeight.percent(125), textOverflow: TextOverflow.ellipsis, - margin: Margins.only(left: 0, right: 0), - backgroundColor: Theme.of(context).colorScheme.surface, + margin: Margins.zero, + ), + 'ol': Style( + fontSize: fontSize != null ? FontSize(fontSize!) : null, + maxLines: maxLines, + lineHeight: LineHeight.percent(125), + textOverflow: TextOverflow.ellipsis, + margin: Margins.zero, + ), + 'ul': Style( + fontSize: fontSize != null ? FontSize(fontSize!) : null, + maxLines: maxLines, + lineHeight: LineHeight.percent(125), + textOverflow: TextOverflow.ellipsis, + margin: Margins.zero, ), 'table': Style( + margin: Margins.zero, height: Height.auto(), width: Width.auto(), border: Border( @@ -135,6 +153,7 @@ class ZdsHtml extends StatelessWidget { ), ), 'tr': Style( + margin: Margins.zero, height: Height.auto(), width: Width.auto(), border: Border( @@ -145,6 +164,7 @@ class ZdsHtml extends StatelessWidget { ), ), 'th': Style( + margin: Margins.zero, height: Height.auto(), width: Width.auto(), border: Border( @@ -156,6 +176,7 @@ class ZdsHtml extends StatelessWidget { padding: HtmlPaddings.all(6), ), 'td': Style( + margin: Margins.zero, height: Height.auto(), width: Width.auto(), padding: HtmlPaddings.all(6), @@ -167,12 +188,29 @@ class ZdsHtml extends StatelessWidget { top: BorderSide(color: colorscheme.onSurface, width: 0.5), ), ), + 'h3': Style(margin: Margins.zero), + 'h4': Style(margin: Margins.zero), 'h5': Style(maxLines: maxLines, textOverflow: TextOverflow.ellipsis), 'iframe': Style(width: Width(box.maxWidth), height: Height((box.maxWidth / 16) * 9)), 'figure': Style(width: Width(box.maxWidth)), 'blockquote': Style( - padding: HtmlPaddings.only(left: 16), - border: Border(left: BorderSide(color: Zeta.of(context).colors.borderSubtle, width: 5)), + margin: Margins.zero, + padding: HtmlPaddings.symmetric(horizontal: 14, vertical: 8), + border: Border(left: BorderSide(color: zetaColors.borderDisabled, width: 4)), + color: zetaColors.textSubtle, + ), + 'pre': Style( + margin: Margins.zero, + padding: HtmlPaddings.all(14), + backgroundColor: zetaColors.surfaceTertiary, + lineHeight: LineHeight.percent(100), + color: zetaColors.primary.selected, + ), + 'code': Style( + margin: Margins.zero, + backgroundColor: zetaColors.surfaceTertiary, + lineHeight: LineHeight.percent(100), + color: zetaColors.primary, ), ...style, }; diff --git a/lib/src/components/organisms/quill_editor/quill_delta.dart b/lib/src/components/organisms/quill_editor/quill_delta.dart index 898ba78..5f8e94e 100644 --- a/lib/src/components/organisms/quill_editor/quill_delta.dart +++ b/lib/src/components/organisms/quill_editor/quill_delta.dart @@ -1,4 +1,5 @@ // ignore_for_file: avoid_dynamic_calls + import 'package:flutter_quill/flutter_quill.dart'; import 'package:flutter_quill/quill_delta.dart'; import 'package:vsc_quill_delta_to_html/vsc_quill_delta_to_html.dart'; @@ -26,7 +27,7 @@ class ZdsQuillDelta { /// Converts the underlying Quill's Delta to an HTML string. String toHtml() { // Convert document to JSON format - final List deltaJson = document.toDelta().toJson(); + final List> deltaJson = _correctJsonArray(document.toDelta().toJson()); // Iterate through each Delta operation for (final dynamic element in deltaJson) { @@ -38,8 +39,11 @@ class ZdsQuillDelta { } // Use the QuillDeltaToHtmlConverter to convert modified Delta to HTML - final QuillDeltaToHtmlConverter converter = - QuillDeltaToHtmlConverter(List.castFrom(deltaJson), ConverterOptions.forEmail()); + final QuillDeltaToHtmlConverter converter = QuillDeltaToHtmlConverter( + List.castFrom(deltaJson), + ConverterOptions.forEmail(), + ); + return converter.convert(); } @@ -51,6 +55,47 @@ class ZdsQuillDelta { } } + List> _correctJsonArray(List> inputArray) { + final List> correctedArray = []; + + bool isBlock(dynamic attributes) { + return (attributes != null && attributes is Map) && + (attributes.containsKey('header') || + attributes.containsKey('list') || + attributes.containsKey('blockquote') || + attributes.containsKey('code-block') || + attributes.containsKey('direction') || + attributes.containsKey('align') || + attributes.containsKey('indent')); + } + + for (final element in inputArray) { + if (element['insert'] != '\n' && element['attributes'] != null && isBlock(element['attributes'])) { + final insertValue = element['insert']; + final attributes = element['attributes']; + + if (insertValue is String && insertValue.contains('\n')) { + final parts = insertValue.split('\n'); + for (int i = 0; i < parts.length; i++) { + if (parts[i].isNotEmpty) { + correctedArray + ..add({'insert': parts[i]}) + ..add({'attributes': attributes, 'insert': '\n'}); + } + } + } else { + correctedArray + ..add({'insert': insertValue}) + ..add({'attributes': attributes, 'insert': '\n'}); + } + } else { + correctedArray.add(element); + } + } + + return correctedArray; + } + /// Creates a copy with provided document ZdsQuillDelta copyWith({ Document? document, diff --git a/pubspec.yaml b/pubspec.yaml index bac1ed4..a5529df 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -71,7 +71,7 @@ dependencies: validators: ^3.0.0 video_compress: ^3.1.2 video_player: ^2.8.6 - vsc_quill_delta_to_html: ^1.0.4 + vsc_quill_delta_to_html: ^1.0.5 zeta_flutter: ^0.11.1 dev_dependencies: