mirror of
https://github.com/AppFlowy-IO/AppFlowy.git
synced 2024-08-30 18:12:39 +00:00
feat: paste html rich text inside text
This commit is contained in:
@ -15,12 +15,13 @@ class HTMLConverter {
|
|||||||
final result = <Node>[];
|
final result = <Node>[];
|
||||||
final delta = Delta();
|
final delta = Delta();
|
||||||
|
|
||||||
for (final child in _document.body?.nodes.toList() ?? <html.Node>[]) {
|
final childNodes = _document.body?.nodes.toList() ?? <html.Node>[];
|
||||||
|
for (final child in childNodes) {
|
||||||
if (child is html.Element) {
|
if (child is html.Element) {
|
||||||
if (child.localName == "span") {
|
if (child.localName == "a" ||
|
||||||
delta.insert(child.text);
|
child.localName == "span" ||
|
||||||
} else if (child.localName == "strong") {
|
child.localName == "strong") {
|
||||||
delta.insert(child.text, {"bold": true});
|
_handleRichTextElement(delta, child);
|
||||||
} else {
|
} else {
|
||||||
_handleElement(result, child);
|
_handleElement(result, child);
|
||||||
}
|
}
|
||||||
@ -59,6 +60,25 @@ class HTMLConverter {
|
|||||||
}
|
}
|
||||||
|
|
||||||
_handleParagraph(List<Node> nodes, html.Element element) {
|
_handleParagraph(List<Node> nodes, html.Element element) {
|
||||||
|
_handleRichText(nodes, element);
|
||||||
|
}
|
||||||
|
|
||||||
|
_handleRichTextElement(Delta delta, html.Element element) {
|
||||||
|
if (element.localName == "span") {
|
||||||
|
delta.insert(element.text);
|
||||||
|
} else if (element.localName == "a") {
|
||||||
|
final hyperLink = element.attributes["href"];
|
||||||
|
Map<String, dynamic>? attributes;
|
||||||
|
if (hyperLink != null) {
|
||||||
|
attributes = {"href": hyperLink};
|
||||||
|
}
|
||||||
|
delta.insert(element.text, attributes);
|
||||||
|
} else if (element.localName == "strong") {
|
||||||
|
delta.insert(element.text, {"bold": true});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
_handleRichText(List<Node> nodes, html.Element element) {
|
||||||
final image = element.querySelector("img");
|
final image = element.querySelector("img");
|
||||||
if (image != null) {
|
if (image != null) {
|
||||||
_handleImage(nodes, image);
|
_handleImage(nodes, image);
|
||||||
@ -69,13 +89,10 @@ class HTMLConverter {
|
|||||||
|
|
||||||
for (final child in element.nodes.toList()) {
|
for (final child in element.nodes.toList()) {
|
||||||
if (child is html.Element) {
|
if (child is html.Element) {
|
||||||
if (child.localName == "a") {
|
if (child.localName == "a" ||
|
||||||
final hyperLink = child.attributes["href"];
|
child.localName == "span" ||
|
||||||
Map<String, dynamic>? attributes;
|
child.localName == "strong") {
|
||||||
if (hyperLink != null) {
|
_handleRichTextElement(delta, element);
|
||||||
attributes = {"href": hyperLink};
|
|
||||||
}
|
|
||||||
delta.insert(child.text, attributes);
|
|
||||||
} else {
|
} else {
|
||||||
delta.insert(child.text);
|
delta.insert(child.text);
|
||||||
}
|
}
|
||||||
@ -122,11 +139,11 @@ class HTMLConverter {
|
|||||||
}
|
}
|
||||||
|
|
||||||
_handleListElement(List<Node> nodes, html.Element element) {
|
_handleListElement(List<Node> nodes, html.Element element) {
|
||||||
final delta = Delta();
|
final childNodes = element.nodes.toList();
|
||||||
delta.insert(element.text);
|
for (final child in childNodes) {
|
||||||
if (delta.operations.isNotEmpty) {
|
if (child is html.Element) {
|
||||||
nodes.add(TextNode(
|
_handleRichText(nodes, child);
|
||||||
type: "text", attributes: {"subtype": "bullet-list"}, delta: delta));
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -10,8 +10,6 @@ _handleCopy() async {
|
|||||||
}
|
}
|
||||||
|
|
||||||
_pasteHTML(EditorState editorState, String html) {
|
_pasteHTML(EditorState editorState, String html) {
|
||||||
final converter = HTMLConverter(html);
|
|
||||||
final nodes = converter.toNodes();
|
|
||||||
final selection = editorState.cursorSelection;
|
final selection = editorState.cursorSelection;
|
||||||
if (selection == null) {
|
if (selection == null) {
|
||||||
return;
|
return;
|
||||||
@ -21,9 +19,31 @@ _pasteHTML(EditorState editorState, String html) {
|
|||||||
if (path.isEmpty) {
|
if (path.isEmpty) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
path[path.length - 1]++;
|
|
||||||
|
final converter = HTMLConverter(html);
|
||||||
|
final nodes = converter.toNodes();
|
||||||
|
|
||||||
|
if (nodes.isEmpty) {
|
||||||
|
return;
|
||||||
|
} else if (nodes.length == 1) {
|
||||||
|
final firstNode = nodes[0];
|
||||||
|
final nodeAtPath = editorState.document.nodeAtPath(path)!;
|
||||||
|
final tb = TransactionBuilder(editorState);
|
||||||
|
final startOffset = selection.start.offset;
|
||||||
|
if (nodeAtPath.type == "text" && firstNode.type == "text") {
|
||||||
|
final textNodeAtPath = nodeAtPath as TextNode;
|
||||||
|
final firstTextNode = firstNode as TextNode;
|
||||||
|
tb.textEdit(textNodeAtPath,
|
||||||
|
() => Delta().retain(startOffset).concat(firstTextNode.delta));
|
||||||
|
tb.setAfterSelection(Selection.collapsed(Position(
|
||||||
|
path: path, offset: startOffset + firstTextNode.delta.length)));
|
||||||
|
}
|
||||||
|
tb.commit();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
final tb = TransactionBuilder(editorState);
|
final tb = TransactionBuilder(editorState);
|
||||||
|
path[path.length - 1]++;
|
||||||
tb.insertNodes(path, nodes);
|
tb.insertNodes(path, nodes);
|
||||||
tb.commit();
|
tb.commit();
|
||||||
}
|
}
|
||||||
@ -98,6 +118,7 @@ _handlePastePlainText(EditorState editorState, String plainText) {
|
|||||||
tb.insertNodes(path, nodes);
|
tb.insertNodes(path, nodes);
|
||||||
tb.commit();
|
tb.commit();
|
||||||
|
|
||||||
|
// fixme: don't set the cursor manually
|
||||||
editorState.updateCursorSelection(Selection.collapsed(
|
editorState.updateCursorSelection(Selection.collapsed(
|
||||||
Position(path: nodes.last.path, offset: lines.last.length)));
|
Position(path: nodes.last.path, offset: lines.last.length)));
|
||||||
}
|
}
|
||||||
|
Reference in New Issue
Block a user