Skip to content
Open
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
21 changes: 20 additions & 1 deletion lib/parser/default_html_to_ops.dart
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,9 @@ import 'package:dart_quill_delta/dart_quill_delta.dart';
import 'package:flutter_quill_delta_from_html/parser/extensions/node_ext.dart';
import 'package:flutter_quill_delta_from_html/parser/html_to_operation.dart';
import 'package:flutter_quill_delta_from_html/parser/html_utils.dart';
import 'package:flutter_quill_delta_from_html/parser/node_processor.dart';
import 'package:flutter_quill_delta_from_html/parser/typedef/typedefs.dart';
import 'package:html/dom.dart' as dom;
import 'package:flutter_quill_delta_from_html/parser/node_processor.dart';

/// Default implementation of `HtmlOperations` for converting common HTML to Delta operations.
///
Expand Down Expand Up @@ -59,10 +59,19 @@ class DefaultHtmlToOperations extends HtmlOperations {
}
inlineAttributes.addAll(styleAttributes);
}

// Check if paragraph is inside a list item
final isInsideListItem = element.parent?.localName == 'li';

final nodes = element.nodes;
//this store into all nodes into a paragraph, and
//ensure getting all attributes or tags into a paragraph
for (final node in nodes) {
// Skip <br> tags if inside a list item
if (isInsideListItem && node is dom.Element && node.isBreakLine) {
continue;
}

processNode(
node,
inlineAttributes,
Expand All @@ -75,6 +84,12 @@ class DefaultHtmlToOperations extends HtmlOperations {
if (blockAttributes.isNotEmpty) {
blockAttributes.removeWhere((key, value) => value == null);
delta.insert('\n', blockAttributes);
} else {
// Insert newline if parent is not a list, OR if parent is a list but current element is not the last child of <li>
final parent = element.parent;
if (parent?.isList == false) {
delta.insert('\n');
}
}

return delta.toList();
Expand Down Expand Up @@ -239,6 +254,10 @@ class DefaultHtmlToOperations extends HtmlOperations {
final List<dom.Element> items =
element.children.where((child) => child.localName == 'li').toList();

final startAttr = element.attributes["start"];
if (startAttr != null) {
attributes["start"] = startAttr;
}
if (tagName == 'ul') {
attributes['list'] = 'bullet';
} else if (tagName == 'ol') {
Expand Down
3 changes: 2 additions & 1 deletion lib/parser/html_to_delta.dart
Original file line number Diff line number Diff line change
Expand Up @@ -170,7 +170,8 @@ class HtmlToDelta {
//ensure insert a new line at the final to avoid any conflict with assertions
if (delta.isNotEmpty) {
final Operation lastOpdata = delta.last;
final bool lastDataIsNotNewLine = lastOpdata.data.toString() != '\n';
final bool lastDataIsNotNewLine = lastOpdata.data.toString() != '\n' &&
!lastOpdata.data.toString().endsWith('\n');
final bool hasAttributes = lastOpdata.attributes != null;
if (lastDataIsNotNewLine && hasAttributes ||
lastDataIsNotNewLine ||
Expand Down
46 changes: 30 additions & 16 deletions lib/parser/node_processor.dart
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import 'package:dart_quill_delta/dart_quill_delta.dart';
import 'package:flutter_quill_delta_from_html/flutter_quill_delta_from_html.dart';
import 'package:flutter_quill_delta_from_html/parser/default_html_to_ops.dart';
import 'package:html/dom.dart' as dom;

/// Processes a DOM [node], converting it into Quill Delta operations.
Expand All @@ -21,15 +22,14 @@ import 'package:html/dom.dart' as dom;
/// processNode(htmlNode, {}, delta);
/// print(delta.toJson()); // Output: [{"insert": "Hello, "}, {"insert": "World", "attributes": {"italic": true, "bold": true}}, {"insert": "!"}]
/// ```
void processNode(
dom.Node node,
Map<String, dynamic> attributes,
Delta delta, {
bool addSpanAttrs = false,
List<CustomHtmlPart>? customBlocks,
List<String>? removeTheseAttributesFromSpan,
CSSVarible? onDetectLineheightCssVariable,
}) {
void processNode(dom.Node node,
Map<String, dynamic> attributes,
Delta delta, {
bool addSpanAttrs = false,
List<CustomHtmlPart>? customBlocks,
List<String>? removeTheseAttributesFromSpan,
CSSVarible? onDetectLineheightCssVariable,
}) {
if (node is dom.Text) {
delta.insert(node.text, attributes.isEmpty ? null : attributes);
} else if (node is dom.Element) {
Expand All @@ -42,20 +42,33 @@ void processNode(
if (node.isStrike) newAttributes['strike'] = true;
if (node.isSubscript) newAttributes['script'] = 'sub';
if (node.isSuperscript) newAttributes['script'] = 'super';
bool handledByCustomBlock = false;

// Use custom block definitions if provided
if (customBlocks != null && customBlocks.isNotEmpty) {
for (var customBlock in customBlocks) {
if (customBlock.matches(node)) {
final operations =
customBlock.convert(node, currentAttributes: newAttributes);
customBlock.convert(node, currentAttributes: newAttributes);
operations.forEach((Operation op) {
delta.insert(op.data, op.attributes);
});
handledByCustomBlock = true;
continue;
}
}
} else {
}

if (!handledByCustomBlock) {
final htmlOperations =DefaultHtmlToOperations(onDetectLineheightCssVariable);
if(node.isCodeBlock){

final operations=htmlOperations.codeblockToOp(node);
operations.forEach((Operation op) {
delta.insert(op.data, op.attributes);
});
return;
}
// Handle <span> tags
if (node.isSpan) {
final spanAttributes = parseStyleAttribute(
Expand All @@ -77,6 +90,7 @@ void processNode(
}
}


// Handle <img> tags
if (node.isImg) {
final String src = node.attributes['src'] ?? '';
Expand All @@ -89,11 +103,11 @@ void processNode(
styles.isEmpty
? null
: {
'style': attributes.entries
.map((entry) => '${entry.key}:${entry.value}')
.toList()
.join(';'),
},
'style': attributes.entries
.map((entry) => '${entry.key}:${entry.value}')
.toList()
.join(';'),
},
);
}
}
Expand Down
Loading