mirror of
https://github.com/AppFlowy-IO/AppFlowy.git
synced 2024-08-30 18:12:39 +00:00
fix: delete the nested bulleted list will lost the children nodes
This commit is contained in:
parent
d648f2b5b9
commit
6bda1fd2ea
@ -0,0 +1,46 @@
|
||||
import 'package:appflowy_editor/src/document/node.dart';
|
||||
|
||||
class Infra {
|
||||
// find the forward nearest text node
|
||||
static TextNode? forwardNearestTextNode(Node node) {
|
||||
var previous = node.previous;
|
||||
while (previous != null) {
|
||||
final lastTextNode = findLastTextNode(previous);
|
||||
if (lastTextNode != null) {
|
||||
return lastTextNode;
|
||||
}
|
||||
if (previous is TextNode) {
|
||||
return previous;
|
||||
}
|
||||
previous = previous.previous;
|
||||
}
|
||||
final parent = node.parent;
|
||||
if (parent != null) {
|
||||
if (parent is TextNode) {
|
||||
return parent;
|
||||
}
|
||||
return forwardNearestTextNode(parent);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
// find the last text node
|
||||
static TextNode? findLastTextNode(Node node) {
|
||||
final children = node.children.toList(growable: false).reversed;
|
||||
for (final child in children) {
|
||||
if (child.children.isNotEmpty) {
|
||||
final result = findLastTextNode(child);
|
||||
if (result != null) {
|
||||
return result;
|
||||
}
|
||||
}
|
||||
if (child is TextNode) {
|
||||
return child;
|
||||
}
|
||||
}
|
||||
if (node is TextNode) {
|
||||
return node;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
@ -1,7 +1,9 @@
|
||||
import 'package:appflowy_editor/src/infra/infra.dart';
|
||||
import 'package:appflowy_editor/src/service/internal_key_event_handlers/number_list_helper.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter/services.dart';
|
||||
import 'package:appflowy_editor/appflowy_editor.dart';
|
||||
import 'package:appflowy_editor/src/extensions/path_extensions.dart';
|
||||
|
||||
// Handle delete text.
|
||||
ShortcutEventHandler deleteTextHandler = (editorState, event) {
|
||||
@ -126,33 +128,34 @@ KeyEventResult _backDeleteToPreviousTextNode(
|
||||
List<Node> nonTextNodes,
|
||||
Selection selection,
|
||||
) {
|
||||
// Not reach to the root.
|
||||
// if (textNode.parent?.parent != null) {
|
||||
// transactionBuilder
|
||||
// ..deleteNode(textNode)
|
||||
// ..insertNode(textNode.parent!.path.next, textNode)
|
||||
// ..afterSelection = Selection.collapsed(
|
||||
// Position(path: textNode.parent!.path.next, offset: 0),
|
||||
// )
|
||||
// ..commit();
|
||||
// return KeyEventResult.handled;
|
||||
// }
|
||||
if (textNode.next == null &&
|
||||
textNode.children.isEmpty &&
|
||||
textNode.parent?.parent != null) {
|
||||
transactionBuilder
|
||||
..deleteNode(textNode)
|
||||
..insertNode(textNode.parent!.path.next, textNode)
|
||||
..afterSelection = Selection.collapsed(
|
||||
Position(path: textNode.parent!.path.next, offset: 0),
|
||||
)
|
||||
..commit();
|
||||
return KeyEventResult.handled;
|
||||
}
|
||||
|
||||
bool prevIsNumberList = false;
|
||||
final previousTextNode = forwardNearestTextNode(textNode);
|
||||
final previousTextNode = Infra.forwardNearestTextNode(textNode);
|
||||
if (previousTextNode != null) {
|
||||
if (previousTextNode.subtype == BuiltInAttributeKey.numberList) {
|
||||
prevIsNumberList = true;
|
||||
}
|
||||
|
||||
transactionBuilder.mergeText(previousTextNode, textNode);
|
||||
transactionBuilder.deleteNode(textNode);
|
||||
if (textNode.children.isNotEmpty) {
|
||||
transactionBuilder.insertNodes(
|
||||
previousTextNode.path + [0],
|
||||
previousTextNode.path.next,
|
||||
textNode.children.toList(growable: false),
|
||||
);
|
||||
}
|
||||
transactionBuilder.deleteNode(textNode);
|
||||
transactionBuilder.afterSelection = Selection.collapsed(
|
||||
Position(
|
||||
path: previousTextNode.path,
|
||||
@ -273,46 +276,3 @@ void _deleteTextNodes(TransactionBuilder transactionBuilder,
|
||||
secondOffset: selection.end.offset,
|
||||
);
|
||||
}
|
||||
|
||||
// TODO: Just a simple solution for textNode, need to be optimized.
|
||||
TextNode? findLastTextNode(Node node) {
|
||||
final children = node.children.toList(growable: false).reversed;
|
||||
for (final child in children) {
|
||||
if (child.children.isNotEmpty) {
|
||||
final result = findLastTextNode(child);
|
||||
if (result != null) {
|
||||
return result;
|
||||
}
|
||||
}
|
||||
if (child is TextNode) {
|
||||
return child;
|
||||
}
|
||||
}
|
||||
if (node is TextNode) {
|
||||
return node;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
// find the forward nearest text node
|
||||
TextNode? forwardNearestTextNode(Node node) {
|
||||
var previous = node.previous;
|
||||
while (previous != null) {
|
||||
final lastTextNode = findLastTextNode(previous);
|
||||
if (lastTextNode != null) {
|
||||
return lastTextNode;
|
||||
}
|
||||
if (previous is TextNode) {
|
||||
return previous;
|
||||
}
|
||||
previous = previous.previous;
|
||||
}
|
||||
final parent = node.parent;
|
||||
if (parent != null) {
|
||||
if (parent is TextNode) {
|
||||
return parent;
|
||||
}
|
||||
return forwardNearestTextNode(parent);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
@ -0,0 +1,51 @@
|
||||
import 'package:appflowy_editor/appflowy_editor.dart';
|
||||
import 'package:appflowy_editor/src/infra/infra.dart';
|
||||
import 'package:flutter_test/flutter_test.dart';
|
||||
|
||||
void main() async {
|
||||
group('infra.dart', () {
|
||||
test('find the last text node', () {
|
||||
// * Welcome to Appflowy 😁
|
||||
// * Welcome to Appflowy 😁
|
||||
// * Welcome to Appflowy 😁
|
||||
// * Welcome to Appflowy 😁
|
||||
// * Welcome to Appflowy 😁
|
||||
// * Welcome to Appflowy 😁
|
||||
// * Welcome to Appflowy 😁
|
||||
const text = 'Welcome to Appflowy 😁';
|
||||
TextNode textNode() {
|
||||
return TextNode(
|
||||
type: 'text',
|
||||
delta: Delta()..insert(text),
|
||||
);
|
||||
}
|
||||
|
||||
final node110 = textNode();
|
||||
final node111 = textNode();
|
||||
final node11 = textNode()
|
||||
..insert(node110)
|
||||
..insert(node111);
|
||||
final node10 = textNode();
|
||||
final node1 = textNode()
|
||||
..insert(node10)
|
||||
..insert(node11);
|
||||
final node0 = textNode();
|
||||
final node = textNode()
|
||||
..insert(node0)
|
||||
..insert(node1);
|
||||
|
||||
expect(Infra.findLastTextNode(node)?.path, [1, 1, 1]);
|
||||
expect(Infra.findLastTextNode(node0)?.path, [0]);
|
||||
expect(Infra.findLastTextNode(node1)?.path, [1, 1, 1]);
|
||||
expect(Infra.findLastTextNode(node10)?.path, [1, 0]);
|
||||
expect(Infra.findLastTextNode(node11)?.path, [1, 1, 1]);
|
||||
|
||||
expect(Infra.forwardNearestTextNode(node111)?.path, [1, 1, 0]);
|
||||
expect(Infra.forwardNearestTextNode(node110)?.path, [1, 1]);
|
||||
expect(Infra.forwardNearestTextNode(node11)?.path, [1, 0]);
|
||||
expect(Infra.forwardNearestTextNode(node10)?.path, [1]);
|
||||
expect(Infra.forwardNearestTextNode(node1)?.path, [0]);
|
||||
expect(Infra.forwardNearestTextNode(node0)?.path, []);
|
||||
});
|
||||
});
|
||||
}
|
@ -3,7 +3,6 @@ import 'dart:collection';
|
||||
import 'package:appflowy_editor/appflowy_editor.dart';
|
||||
import 'package:appflowy_editor/src/render/image/image_node_widget.dart';
|
||||
import 'package:appflowy_editor/src/render/rich_text/flowy_rich_text.dart';
|
||||
import 'package:appflowy_editor/src/service/internal_key_event_handlers/backspace_handler.dart';
|
||||
import 'package:flutter/services.dart';
|
||||
import 'package:flutter_test/flutter_test.dart';
|
||||
import 'package:network_image_mock/network_image_mock.dart';
|
||||
@ -383,73 +382,26 @@ void main() async {
|
||||
// * Welcome to Appflowy 😁
|
||||
// After
|
||||
// * Welcome to Appflowy 😁
|
||||
// * Welcome to Appflowy 😁Welcome to Appflowy 😁
|
||||
// * Welcome to Appflowy 😁
|
||||
// * Welcome to Appflowy 😁
|
||||
// Welcome to Appflowy 😁
|
||||
// * Welcome to Appflowy 😁
|
||||
// * Welcome to Appflowy 😁
|
||||
await editor.pressLogicKey(LogicalKeyboardKey.backspace);
|
||||
expect(
|
||||
editor.nodeAtPath([1])!.subtype != BuiltInAttributeKey.bulletedList,
|
||||
editor.nodeAtPath([0, 0])!.subtype == BuiltInAttributeKey.bulletedList,
|
||||
true,
|
||||
);
|
||||
expect(
|
||||
editor.nodeAtPath([1, 0])!.subtype == BuiltInAttributeKey.bulletedList,
|
||||
(editor.nodeAtPath([0, 0]) as TextNode).toRawString() == text * 2,
|
||||
true,
|
||||
);
|
||||
expect(
|
||||
editor.nodeAtPath([1, 1])!.subtype == BuiltInAttributeKey.bulletedList,
|
||||
editor.nodeAtPath([0, 1])!.subtype == BuiltInAttributeKey.bulletedList,
|
||||
true,
|
||||
);
|
||||
expect(
|
||||
editor.nodeAtPath([0, 2])!.subtype == BuiltInAttributeKey.bulletedList,
|
||||
true,
|
||||
);
|
||||
|
||||
// After
|
||||
// * Welcome to Appflowy 😁
|
||||
// * Welcome to Appflowy 😁Welcome to Appflowy 😁
|
||||
// * Welcome to Appflowy 😁
|
||||
// * Welcome to Appflowy 😁
|
||||
});
|
||||
|
||||
test('find the last text node', () {
|
||||
// * Welcome to Appflowy 😁
|
||||
// * Welcome to Appflowy 😁
|
||||
// * Welcome to Appflowy 😁
|
||||
// * Welcome to Appflowy 😁
|
||||
// * Welcome to Appflowy 😁
|
||||
// * Welcome to Appflowy 😁
|
||||
// * Welcome to Appflowy 😁
|
||||
const text = 'Welcome to Appflowy 😁';
|
||||
TextNode textNode() {
|
||||
return TextNode(
|
||||
type: 'text',
|
||||
delta: Delta()..insert(text),
|
||||
);
|
||||
}
|
||||
|
||||
final node110 = textNode();
|
||||
final node111 = textNode();
|
||||
final node11 = textNode()
|
||||
..insert(node110)
|
||||
..insert(node111);
|
||||
final node10 = textNode();
|
||||
final node1 = textNode()
|
||||
..insert(node10)
|
||||
..insert(node11);
|
||||
final node0 = textNode();
|
||||
final node = textNode()
|
||||
..insert(node0)
|
||||
..insert(node1);
|
||||
|
||||
expect(findLastTextNode(node)?.path, [1, 1, 1]);
|
||||
expect(findLastTextNode(node0)?.path, [0]);
|
||||
expect(findLastTextNode(node1)?.path, [1, 1, 1]);
|
||||
expect(findLastTextNode(node10)?.path, [1, 0]);
|
||||
expect(findLastTextNode(node11)?.path, [1, 1, 1]);
|
||||
|
||||
expect(forwardNearestTextNode(node111)?.path, [1, 1, 0]);
|
||||
expect(forwardNearestTextNode(node110)?.path, [1, 1]);
|
||||
expect(forwardNearestTextNode(node11)?.path, [1, 0]);
|
||||
expect(forwardNearestTextNode(node10)?.path, [1]);
|
||||
expect(forwardNearestTextNode(node1)?.path, [0]);
|
||||
expect(forwardNearestTextNode(node0)?.path, []);
|
||||
});
|
||||
}
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user