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

Combination of styles numberList and text #1940

Closed
1 task done
Leonardo1952 opened this issue Jun 21, 2024 · 33 comments
Closed
1 task done

Combination of styles numberList and text #1940

Leonardo1952 opened this issue Jun 21, 2024 · 33 comments
Labels
bug Something isn't working

Comments

@Leonardo1952
Copy link

Is there an existing issue for this?

Flutter Quill version

9.4.4

Steps to reproduce

Create a normal text, then try to create a numbered list, the list will start to behave strangely.

Expected results

Make sure the spaces and numbering between items are correct.

Actual results

The numbers don't match and unnecessary spaces are added.

Code sample

Code sample
QuillToolbar.simple(
          configurations: QuillSimpleToolbarConfigurations(
            controller: controllerQuill,
            multiRowsDisplay: true,
            showAlignmentButtons: false,
            showSmallButton: false,
            showDirection: false,
            showLeftAlignment: false,
            showColorButton: false,
            showBackgroundColorButton: false,
            toolbarIconAlignment: WrapAlignment.start,
            toolbarIconCrossAlignment: WrapCrossAlignment.start,
            showFontFamily: false,
            showSubscript: false,
            showSuperscript: false,
            showFontSize: false,
            showDividers: false,
            showHeaderStyle: false,
            showListNumbers: true,
            showListBullets: false,
            showListCheck: false,
            showCodeBlock: false,
            showQuote: false,
            showStrikeThrough: false,
            showUnderLineButton: false,
            showIndent: false,
            showSearchButton: false,
            buttonOptions: QuillSimpleToolbarButtonOptions(
              bold: styleButtonOption(),
              italic: styleButtonOption(),
              underLine: styleButtonOption(),
              strikeThrough: styleButtonOption(),
              inlineCode: styleButtonOption(),
              subscript: styleButtonOption(),
              superscript: styleButtonOption(),
              small: styleButtonOption(),
              direction: styleButtonOption(),
              listNumbers: styleButtonOption(),
              listBullets: styleButtonOption(),
              quote: styleButtonOption(),
              clipboardCopy: styleButtonOption(),
              clipboardCut: styleButtonOption(),
              clipboardPaste: styleButtonOption(),
              codeBlock: QuillToolbarToggleStyleButtonOptions(
                iconData: FeatherIcons.terminal,
                iconTheme: iconTheme(),
              ),
              selectHeaderStyleButtons:
                  QuillToolbarSelectHeaderStyleButtonsOptions(
                      iconTheme: iconTheme()),
              selectHeaderStyleDropdownButton:
                  QuillToolbarSelectHeaderStyleDropdownButtonOptions(
                      iconTheme: iconTheme()),
              fontSize: QuillToolbarFontSizeButtonOptions(
                iconSize: PocketSizes.iconXSmall,
              ),
              search: QuillToolbarSearchButtonOptions(
                iconSize: PocketSizes.iconXSmall,
                iconTheme: iconTheme(),
              ),
              linkStyle: QuillToolbarLinkStyleButtonOptions(
                iconSize: PocketSizes.iconXSmall,
                iconTheme: iconTheme(),
              ),
              indentIncrease: QuillToolbarIndentButtonOptions(
                iconSize: PocketSizes.iconXSmall,
                iconTheme: iconTheme(),
              ),
              indentDecrease: QuillToolbarIndentButtonOptions(
                iconSize: PocketSizes.iconXSmall,
                iconTheme: iconTheme(),
              ),
              toggleCheckList: QuillToolbarToggleCheckListButtonOptions(
                iconSize: PocketSizes.iconXSmall,
                iconTheme: iconTheme(),
              ),
              selectAlignmentButtons: QuillToolbarSelectAlignmentButtonOptions(
                iconSize: PocketSizes.iconXSmall,
                iconTheme: iconTheme(),
              ),
              undoHistory: QuillToolbarHistoryButtonOptions(
                iconSize: PocketSizes.iconXSmall,
                iconTheme: iconTheme(),
              ),
              redoHistory: QuillToolbarHistoryButtonOptions(
                iconSize: PocketSizes.iconXSmall,
                iconTheme: iconTheme(),
              ),
              clearFormat: QuillToolbarClearFormatButtonOptions(
                iconSize: PocketSizes.iconXSmall,
                iconTheme: iconTheme(),
              ),
            ),
            toolbarSectionSpacing: -0.9,
          ),
        ),```

</details>


### Screenshots or Video

<details>
<summary>Screenshots / Video demonstration</summary>

When combined with other texts
![Screenshot 2024-06-21 103438](https://github.com/singerdmx/flutter-quill/assets/53130191/e8ce8529-25b5-4e74-8b0f-640cf620826f)

Only using the numbered list
![Screenshot 2024-06-21 103523](https://github.com/singerdmx/flutter-quill/assets/53130191/d244cd10-f6ed-4f53-8c6b-f213631bce1a)


</details>


### Logs

<details><summary>Logs</summary>

```console
<p>Shopping list:</p><ol><li><br/></li><li><br/></li><li>orange</li><li>bean</li><li>banana</li><li>potato</li></ol><p><br/><br/></p>
@scolnet
Copy link

scolnet commented Jun 28, 2024

Hello Team,
With recent versions of flutter_quill (9.4.6, 9.4.7 on web or mobile) there is a regression with format delta document when using a block-level.

When you insert a simple text following by a list, blockquote or code-block, the delta generating by the editor is incorrect.
If display is OK with the editor, the html conversion is broken because you have some \n wrong inserted in document.

Example of generated delta :

[
   {
      "insert":"Text\n"
   },
   {
      "insert":"bullet1\n",
      "attributes":{
         "list":"ordered"
      }
   },
   {
      "insert":"bullet2"
   },
   {
      "insert":"\n",
      "attributes":{
         "list":"ordered"
      }
   },
   {
      "insert":"bullet3"
   },
   {
      "insert":"\n",
      "attributes":{
         "list":"ordered"
      }
   },
   {
      "insert":"bullet4"
   },
   {
      "insert":"\n",
      "attributes":{
         "list":"ordered"
      }
   },
   {
      "insert":"\n"
   }
]

The bullet1 don't respect delta format

So when using vcs_quill_delta_to_html, the html is wrong :

<p>Text</p><ol><li><br/></li><li><br/></li><li>bullet2</li><li>bullet3</li><li>bullet4</li></ol><p><br/></p>

The version of flutter_quill 8.6.4 used by the app example in vcs_quill_delta_to_html is ok :

[
  {
    "insert": "text\nbullet 1"
  },
  {
    "insert": "\n",
    "attributes": {
      "list": "ordered"
    }
  },
  {
    "insert": "bullet 2"
  },
  {
    "insert": "\n",
    "attributes": {
      "list": "ordered"
    }
  },
  ...
]

Do you confirm this ?

Thanks for your help

@Leonardo1952
Copy link
Author

@scolnet I noticed that the problem occurs due to the delta at the time of conversions. But so far I have not been able to solve my problem. Have you managed to do so?

@scolnet
Copy link

scolnet commented Jul 3, 2024

Hello @Leonardo1952
After analyse,
To reproduce the case :

  1. Start with a simple text and tap return
  2. Click on a list (or blockquote, codeblock) button
  3. When you enter the text of the first bullet -> the delta is corrupted, so the generated html is wrong.

Note that it's ok if you tap the text of the bullet before and after format with list button.

I found the problem occurs in the insert rule : PreserveInlineStylesRule (/lib/src/models/rules/insert.dart)
I fix it by adding this:

 if(attributes!=null && (attributes.containsKey(Attribute.list.key)||attributes.containsKey(Attribute.codeBlock.key)||attributes.containsKey(Attribute.blockQuote.key))){
      return null;
    }

complete code :

class PreserveInlineStylesRule extends InsertRule {
  const PreserveInlineStylesRule();

  @override
  Delta? applyRule(
    Document document,
    int index, {
    int? len,
    Object? data,
    Attribute? attribute,
  }) {
    if (data is! String || data.contains('\n')) {
      return null;
    }

    final itr = DeltaIterator(document.toDelta());
    var prev = itr.skip(len == 0 ? index : index + 1);

    if (prev == null || prev.data is! String) return null;

    if ((prev.data as String).endsWith('\n')) {
      if (prev.attributes != null) {
        for (final key in prev.attributes!.keys) {
          if (!Attribute.inlineKeys.contains(key)) {
            return null;
          }
        }
      }
      prev = itr
          .next(); // at the start of a line, apply the style for the current line and not the style for the preceding line
    }

    final attributes = prev.attributes;
    if(attributes!=null && (attributes.containsKey(Attribute.list.key)||attributes.containsKey(Attribute.codeBlock.key)||attributes.containsKey(Attribute.blockQuote.key))){
      return null;
    }
    final text = data;
    if (attributes == null || !attributes.containsKey(Attribute.link.key)) {
      return Delta()
        ..retain(index + (len ?? 0))
        ..insert(text, attributes);
    }

    attributes.remove(Attribute.link.key);
    final delta = Delta()
      ..retain(index + (len ?? 0))
      ..insert(text, attributes.isEmpty ? null : attributes);
    final next = itr.next();

    final nextAttributes = next.attributes ?? const <String, dynamic>{};
    if (!nextAttributes.containsKey(Attribute.link.key)) {
      return delta;
    }
    if (attributes[Attribute.link.key] == nextAttributes[Attribute.link.key]) {
      return Delta()
        ..retain(index + (len ?? 0))
        ..insert(text, attributes);
    }
    return delta;
  }
}

It's a bit complex, I don't know the algorithm well enough to ensure that it's the right correction to do.
If we can have some help @singerdmx, @Alspb ?
Thanks a lot

@Alspb
Copy link
Collaborator

Alspb commented Jul 5, 2024

@AtlasAutocode made several sound style-related contributions, so maybe he will be able to help with this issue too. I will try to take a look a bit later is it's not resolved.

@AtlasAutocode
Copy link
Collaborator

I'm currently working on other fixes in PreserveInlineStylesRule.
I think those fixes might also resolve this problem, but I will need to check.

I'm not familiar with correct content of deltas and conversion to html.
Can you tell me what the corrupted deltas look like?

@AtlasAutocode
Copy link
Collaborator

 if(attributes!=null && (attributes.containsKey(Attribute.list.key)||attributes.containsKey(Attribute.codeBlock.key)||attributes.containsKey(Attribute.blockQuote.key))){
      return null;
    }

I also use 'return null' earlier in the code. However, I have found that this is not correct.
Try entering a list with 2 lines of bold text. Move to the start of the second line and enter a letter - it's entered as plain because the null return wipes out the inline styles.

I cannot reproduce the error in this posting with my current code. I need to go back to the released version to compare.
Can you also check whether you see the same error with other block styles. I dislike only fixing list and codeblock if it also applies to other block styles. Thanks.

@AtlasAutocode
Copy link
Collaborator

Have submitted a PR that should resolve this issue.
Let me know if there are any issues.

@CatHood0 CatHood0 closed this as completed Jul 8, 2024
@scolnet
Copy link

scolnet commented Jul 8, 2024

After first tests, it seems OK
Thanks @AtlasAutocode ;)

@BullsEye34
Copy link

Hi, this issue seems to be repeating again.

flutter_quill: 10.0.0

Screenshot 2024-07-18 at 5 38 55 PM

Above is my Text.

[{"insert":"Text\n\n"},{"insert":"First\n","attributes":{"list":"bullet"}},{"insert":"Second"},{"insert":"\n","attributes":{"list":"bullet"}},{"insert":"\n"},{"insert":"1st\n","attributes":{"list":"ordered"}},{"insert":"2nd"},{"insert":"\n","attributes":{"list":"ordered"}},{"insert":"\n"}]
Is the delta it generated

<div><p>Text<br/></p><ul><li><br/></li><li><br/></li><li>Second</li></ul><p><br/></p><ol><li><br/></li><li><br/></li><li>2nd</li></ol><p><br/></p></div>
Is the HTML I am getting back.

@AtlasAutocode
Copy link
Collaborator

Did you run 'pub upgrade' to ensure that all dependencies are updated?

I believe HTML output is no longer a part of the core quill editor. It would be helpful to know how you are producing the output since it appears that the fault is with the Delta-to-HTML code.
@CatHood0 can you take a look at the HTML output? It appears the text for 'First' and '1st' is being lost.

@AtlasAutocode AtlasAutocode reopened this Jul 18, 2024
@dsyrstad
Copy link
Contributor

@AtlasAutocode The problem is not with the HTML, it's with the Delta being generated by flutter_quill. List and blockquotes are block-level attributes and must be applied on an op with a \n (indicating the end of a block). This bug is also a regression because this package used to generate the correct delta in around 8.6.x.

@BullsEye34
Copy link

I agree with @dsyrstad .

If we observe the generated Delta properly,
The first item says:
{"insert":"First\n","attributes":{"list":"bullet"}}

Whereas it must have been:
{"insert":"First"},{"insert":"\n","attributes":{"list":"bullet"}}

Same goes with the ordered list as well.

@scolnet
Copy link

scolnet commented Jul 18, 2024

Hello everyone,

I didn't do a full of tests with 10.0.0 yet.

@AtlasAutocode : I see a new commit in PreserveInlineStyleRule since your last fix by @CatHood0 (2 days ago)
d2e3784
Maybe a clue ?

@AtlasAutocode
Copy link
Collaborator

@dsyrstad your knowledge of deltas is better than mine - would you be able to find the problem and fix it?

@scolnet that would narrow our focus - I suspect that this may be a deeper problem in delta creation (and out of my experience - so much to learn - life is good!)

@EchoEllet the first line entry for a list is not generating the correct delta. Do we have an expert in debugging deltas that can help?

@EchoEllet
Copy link
Collaborator

the first line entry for a list is not generating the correct delta. Do we have an expert in debugging deltas that can help?

It seems that most of the delta experts are no longer contributors to the project for now. I'm not quite familiar with Delta.

This is one reason why we need tests and more docs.

@dsyrstad
Copy link
Contributor

@dsyrstad your knowledge of deltas is better than mine - would you be able to find the problem and fix it?

I don't have the time. However there is documentation on https://quilljs.com/docs. It is also the defacto standard for quill delta, so whatever it generates for given input can be used as a reference for flutter_quill.

@CatHood0
Copy link
Collaborator

@AtlasAutocode : I see a new commit in PreserveInlineStyleRule since your last fix by @CatHood0 (2 days ago)

It's not related. I just fix an issue with the Rule assume the type of the operation and it could throw exceptions.

@CatHood0
Copy link
Collaborator

CatHood0 commented Jul 19, 2024

@BullsEye34

Your Delta has certain errors to take into account.

The Deltas have a great characteristic that they can contain attributes. These attributes have two levels.

Inline attributes

This level will only apply to the object that is inside the insert.

You could take this Delta as an example:

[
  {"insert":"Text "},
  {"insert":"Colored","attributes":{"color":"<your_color>"}},
  {"insert":"\n"}
]

As you can imagine, the only formatting that this sentence will have will only be for the "Colored" part of the text which will have a color applied depending on the value inserted in the attribute.

As a general rule, insertions with inline attributes will not have new lines within the object they contain (because it is not necessary), although I can't really say if this last case is 100% true, since I never read the documentation.

Block attributes

These attributes are always applied at the end after any text. That is, these cannot be at the same level as the text, nor can they be applied to an insertion that is not just an insertion with one or more new lines (take this as an example: {"insert":"\n","attributes": {"list":"bullet"}} )

These will be applied to any insertion that is above them, although not to all those that are above it, they have limits depending on what. This will be limited to applying as long as the insertion above it does not contain any new line without the same type of attribute. That is, the new lines limit the scope of these block attributes.

We can first take your Delta as an example of what a Delta should NOT be:

[
 {"insert":"Text\n\n"},
 {"insert":"First\n","attributes":{"list":"bullet"}},
 {"insert":"Second"},
 {"insert":"\n","attributes":{"list":"bullet"}},
 {"insert":"\n"},
 {"insert":"1st\n","attributes":{"list":"ordered"}},
 {"insert":"2nd"},
 {"insert":"\n","attributes":{"list":"ordered"}},
 {"insert":"\n"}
]

And if you read the previous thing I wrote, then you'll quickly notice why "First" disappears. This is why I mentioned that block attributes should never have any item or object that has to be shown in the editor, since it will ignore them since it is a rule of the Delta format, since these insertions with block attributes are just to apply these.

So, to have a Delta that DOES NOT eliminate any part of the text and that applies the attributes as they should, your Delta must be such that this:

[
 {"insert":"Text\n\n"},
 {"insert":"First"},
 {"insert":"\n","attributes":{"list":"bullet"}},
 {"insert":"Second"},
 {"insert":"\n","attributes":{"list":"bullet"}},
 {"insert":"\n"},
 {"insert":"1st"},
 {"insert":"\n","attributes":{"list":"bullet"}},
 {"insert":"2nd"},
 {"insert":"\n","attributes":{"list":"ordered"}},
 {"insert":"\n"}
]

Note

An exception to this would be the new duplicate lines, which when found will take all but one of these so that it can be used to correctly apply the style.

[
 {"insert":"\n\n\n","attributes":{"header": 1}},
 {"insert":"\n"}
]

This Delta, for example, will only apply 2 of the new lines with the header style applied. Why only 2? As I mentioned before, this is because inserts that are for applying attributes in bulk need at least one of these new lines to work.

You can always go to it to know more about this

@BullsEye34
Copy link

BullsEye34 commented Jul 19, 2024

I wanted this issue resolved on a priority, so here's a simple function and it seems to work without issues.

Anyone facing the issue and wants to eliminate this particular use case, can use the function below.

List<Map<String, dynamic>> transformArray(List<dynamic> inputArray) {
    final List<Map<String, dynamic>> result = <Map<String, dynamic>>[];
    for (final dynamic item in inputArray) {
      if (item is Map<String, dynamic>) {
        if (item.containsKey("attributes") && item["attributes"] != null) {
          if (item["attributes"]["list"] == "ordered" || item["attributes"]["list"] == "bullet") {
            // Split the item into two separate items
            result
              ..add(<String, dynamic>{"insert": item["insert"].replaceAll("\n", "")})
              ..add(<String, dynamic>{
                "insert": "\n",
                "attributes": <String, dynamic>{"list": item["attributes"]["list"]}
              });
          } else {
            result.add(item);
          }
        } else {
          result.add(item);
        }
      }
    }
    return result;
  }

This is not a permanent solution. Just a workaround until it is fixed.

@CatHood0
Copy link
Collaborator

CatHood0 commented Jul 19, 2024

Anyone facing the issue and wants to eliminate this particular use case, can use the function below.

I don't think that is a good idea. I prefer always use the original format, and, if you need the Delta to transform to HTML, you can use a denormalizer function to solve this without add unnecessary logic. You can take this as a denormalizer (i took this from vsc_quill_delta_from_html since it's useful to me to various of my packages) example for your Deltas

extension DeltaDenormilazer on Delta {
  Delta fullDenormalizer() {
    if (isEmpty) return this;

    final List<Map<String, dynamic>> denormalizedOps =
        map<List<Map<String, dynamic>>>((Operation op) => denormalize(op.toJson())).flattened.toList();
    return Delta.fromOperations(denormalizedOps.map<fq.Operation>((Map<String, dynamic> e) => fq.Operation.fromJson(e)).toList());
  }

  List<Map<String, dynamic>> denormalize(Map<String, dynamic> op) {
    const String newLine = '\n';
    final insertValue = op['insert'];
    if (insertValue is Map || insertValue == newLine) {
      return <Map<String, dynamic>>[op];
    }

    final List<String> newlinedArray = tokenizeWithNewLines(insertValue.toString());

    if (newlinedArray.length == 1) {
      return <Map<String, dynamic>>[op];
    }

    // Copy op in to keep its attributes, but replace the insert value with a newline.
    final Map<String, dynamic> nlObj = <String, dynamic>{
      ...op,
      ...<String, String>{'insert': newLine}
    };

    return newlinedArray.map((String line) {
      if (line == newLine) {
        return nlObj;
      }
      return <String, dynamic>{
        ...op,
        ...<String, String>{'insert': line},
      };
    }).toList();
  }
}


/// Splits a string [str] by new line characters ("\n"), preserving empty lines
/// as separate tokens in the resulting array.
///
/// Example:
/// ```dart
/// String input = "hello\n\nworld\n ";
/// List<String> tokens = tokenizeWithNewLines(input);
/// print(tokens); // Output: ["hello", "\n", "\n", "world", "\n", " "]
/// ```
///
/// Returns a list of strings where each element represents either a line of text
/// or a new line character.
List<String> tokenizeWithNewLines(String str) {
  const String newLine = '\n';

  if (str == newLine) {
    return <String>[str];
  }

  List<String> lines = str.split(newLine);

  if (lines.length == 1) {
    return lines;
  }

  int lastIndex = lines.length - 1;

  return lines.foldIndexed(<String>[], (int ind, List<String> pv, String line) {
    if (ind != lastIndex) {
      if (line != '') {
        pv.add(line);
        pv.add(newLine);
      } else {
        pv.add(newLine);
      }
    } else if (line != '') {
      pv.add(line);
    }
    return pv;
  });
}

@scolnet
Copy link

scolnet commented Jul 19, 2024

@CatHood0
I haven't had any more problem since the fix of @AtlasAutocode.
I test with flutter_quill 10.0.1

@BullsEye34
When I try with your content, the delta generated is Ok.

[{"insert":"Text\n\nFirst"},{"insert":"\n","attributes":{"list":"bullet"}},{"insert":"Second"},{"insert":"\n","attributes":{"list":"bullet"}},{"insert":"\n1st"},{"insert":"\n","attributes":{"list":"ordered"}},{"insert":"2nd"},{"insert":"\n","attributes":{"list":"ordered"}},{"insert":"\n"}]

code HTML :

<p>Text<br/></p><ul><li>First</li><li>Second</li></ul><p><br/></p><ol><li>1st</li><li>2nd</li></ol><p><br/></p>

Could you explain precisely how you enter the data?
You enter text and after format or you format and after your enter text ?
I know the process can change the behavior

@CatHood0
Copy link
Collaborator

CatHood0 commented Jul 19, 2024

@scolnet I don't know where is the issue. The generated HTML it's correct good, and the Delta is correct for the standard. Can you explain where is the problem?

You enter text and after format or you format and after your enter text ?

If you want to apply that Delta to the editor, you can enter it without formatting. I say you could need to format it if you want to do something with the operations and need to make more easy store on them

@scolnet
Copy link

scolnet commented Jul 19, 2024

@CatHood0
I try to reproduce the problem of @BullsEye34
#1940 (comment)

@BullsEye34
Copy link

@CatHood0 I haven't had any more problem since the fix of @AtlasAutocode. I test with flutter_quill 10.0.1

@BullsEye34 When I try with your content, the delta generated is Ok.

[{"insert":"Text\n\nFirst"},{"insert":"\n","attributes":{"list":"bullet"}},{"insert":"Second"},{"insert":"\n","attributes":{"list":"bullet"}},{"insert":"\n1st"},{"insert":"\n","attributes":{"list":"ordered"}},{"insert":"2nd"},{"insert":"\n","attributes":{"list":"ordered"}},{"insert":"\n"}]

code HTML :

<p>Text<br/></p><ul><li>First</li><li>Second</li></ul><p><br/></p><ol><li>1st</li><li>2nd</li></ol><p><br/></p>

Could you explain precisely how you enter the data? You enter text and after format or you format and after your enter text ? I know the process can change the behavior

What I did was:

Type in "-" and space and then the bullet item.
Same with "1." and space for ordered list.

This was reproducible also in this scenario.

On the toolbar, click the button to insert an unordered/ordered list. Start typing.

In both the scenarios, the first list item would have the "\n" appended to the item, therefore converting the same to html would become <li><br/></li><li><br/></li>

@BullsEye34
Copy link

Anyone facing the issue and wants to eliminate this particular use case, can use the function below.

I don't think that is a good idea. I prefer always use the original format, and, if you need the Delta to transform to HTML, you can use a denormalizer function to solve this without add unnecessary logic. You can take this as a denormalizer (i took this from vsc_quill_delta_from_html since it's useful to me to various of my packages) example for your Deltas

extension DeltaDenormilazer on Delta {
  Delta fullDenormalizer() {
    if (isEmpty) return this;

    final List<Map<String, dynamic>> denormalizedOps =
        map<List<Map<String, dynamic>>>((Operation op) => denormalize(op.toJson())).flattened.toList();
    return Delta.fromOperations(denormalizedOps.map<fq.Operation>((Map<String, dynamic> e) => fq.Operation.fromJson(e)).toList());
  }

  List<Map<String, dynamic>> denormalize(Map<String, dynamic> op) {
    const String newLine = '\n';
    final insertValue = op['insert'];
    if (insertValue is Map || insertValue == newLine) {
      return <Map<String, dynamic>>[op];
    }

    final List<String> newlinedArray = tokenizeWithNewLines(insertValue.toString());

    if (newlinedArray.length == 1) {
      return <Map<String, dynamic>>[op];
    }

    // Copy op in to keep its attributes, but replace the insert value with a newline.
    final Map<String, dynamic> nlObj = <String, dynamic>{
      ...op,
      ...<String, String>{'insert': newLine}
    };

    return newlinedArray.map((String line) {
      if (line == newLine) {
        return nlObj;
      }
      return <String, dynamic>{
        ...op,
        ...<String, String>{'insert': line},
      };
    }).toList();
  }
}


/// Splits a string [str] by new line characters ("\n"), preserving empty lines
/// as separate tokens in the resulting array.
///
/// Example:
/// ```dart
/// String input = "hello\n\nworld\n ";
/// List<String> tokens = tokenizeWithNewLines(input);
/// print(tokens); // Output: ["hello", "\n", "\n", "world", "\n", " "]
/// ```
///
/// Returns a list of strings where each element represents either a line of text
/// or a new line character.
List<String> tokenizeWithNewLines(String str) {
  const String newLine = '\n';

  if (str == newLine) {
    return <String>[str];
  }

  List<String> lines = str.split(newLine);

  if (lines.length == 1) {
    return lines;
  }

  int lastIndex = lines.length - 1;

  return lines.foldIndexed(<String>[], (int ind, List<String> pv, String line) {
    if (ind != lastIndex) {
      if (line != '') {
        pv.add(line);
        pv.add(newLine);
      } else {
        pv.add(newLine);
      }
    } else if (line != '') {
      pv.add(line);
    }
    return pv;
  });
}

This is interesting.

Let me try this out. If it is more stable, I'll Definitely use this.
Thank you

@CatHood0
Copy link
Collaborator

Type in "-" and space and then the bullet item.
Same with "1." and space for ordered list.

This was reproducible also in this scenario.

On the toolbar, click the button to insert an unordered/ordered list. Start typing.

I'll take a look about it

@CatHood0
Copy link
Collaborator

Are you using vsc_quill_delta_from_html to generate the HTML?

@BullsEye34
Copy link

I'm using vsc_quill_delta_to_html.

@CatHood0
Copy link
Collaborator

CatHood0 commented Jul 19, 2024

This is werid. I getting the same Delta than you, but, my output is the correct one.

I used the same input text. I'm using the last version of flutter_quill (ignore the wrong spacing it's just bad configs on my app)

Screenshot_20240719-040924

And this is the output (i used directly vsc_quill_delta_to_html without ConverterOptions to show this on my app)

Screenshot_20240719-040934

If you want to know how i get this output, you can use a method like:

import 'package:vsc_quill_delta_to_html/vsc_quill_delta_to_html.dart';

String convertDeltaToHtml(Document doc, [ConverterOptions? options]) {
  return QuillDeltaToHtmlConverter(
    doc.toDelta().toJson(),
    options, // if this doesn't work, try using ConverterOptions.forEmail()
  ).convert();
}

@scolnet
Copy link

scolnet commented Jul 19, 2024

@BullsEye34 Strange, I try with your scenario and i get a correct delta

[{"insert":"Text\n\nFirst"},{"insert":"\n","attributes":{"list":"bullet"}},{"insert":"Second"},{"insert":"\n","attributes":{"list":"bullet"}},{"insert":"\n1st"},{"insert":"\n","attributes":{"list":"ordered"}},{"insert":"2nd"},{"insert":"\n","attributes":{"list":"ordered"}},{"insert":"\n\n"}]

so HTML :

<p>Text<br/></p><ul><li>First</li><li>Second</li></ul><p><br/></p><ol><li>1st</li><li>2nd</li></ol><p><br/></p>

@CatHood0
Copy link
Collaborator

I try with your scenario and i get a correct delta

It's the same for me. I think could be an issue from the vsc_quill_delta_from_html version that is him using or a wrong configuration

@BullsEye34
Copy link

Oh, very weird. Let me try upgrading dependencies....

@BullsEye34
Copy link

My bad. I upgraded my dependencies, and things seem fine now.

Very weird because I had upgraded the deps just yesterday.
Perhaps system cache(pub cache)?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bug Something isn't working
Projects
None yet
Development

No branches or pull requests

8 participants