fix: paste single line text will remove the children block (#1100)

* fix: paste single line text will remove the children block

* fix: ignore deletion in table cell blocl

* chore: update undo redo stack size and seal time

* chore: refactor undo logic

* Revert "chore: refactor undo logic"

This reverts commit 98d64528845eba23f145827d331606afeb2840e5.
This commit is contained in:
Lucas 2025-04-30 15:50:09 +08:00 • committed by GitHub
commit e56746ac65
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
3 changed files with 27 additions and 19 deletions

View file

@ -47,7 +47,12 @@ extension SelectionTransform on EditorState {
/// and deletes the nodes expect for the first node. /// and deletes the nodes expect for the first node.
/// ///
/// For the other cases, this function just deletes all the nodes. /// For the other cases, this function just deletes all the nodes.
Future<bool> deleteSelection(Selection selection) async { Future<bool> deleteSelection(
Selection selection, {
List<String> ignoreNodeTypes = const [
TableCellBlockKeys.type,
],
}) async {
// Nothing to do if the selection is collapsed. // Nothing to do if the selection is collapsed.
if (selection.isCollapsed) { if (selection.isCollapsed) {
return false; return false;
@ -66,7 +71,7 @@ extension SelectionTransform on EditorState {
// or node. // or node.
if (nodes.length == 1) { if (nodes.length == 1) {
// If table cell is selected, clear the cell node child. // If table cell is selected, clear the cell node child.
final node = nodes.first.type == TableCellBlockKeys.type final node = ignoreNodeTypes.contains(nodes.first.type)
? nodes.first.children.first ? nodes.first.children.first
: nodes.first; : nodes.first;
if (node.delta != null) { if (node.delta != null) {
@ -75,7 +80,7 @@ extension SelectionTransform on EditorState {
selection.startIndex, selection.startIndex,
selection.length, selection.length,
); );
} else if (node.parent?.type != TableCellBlockKeys.type) { } else if (!ignoreNodeTypes.contains(node.parent?.type)) {
transaction.deleteNode(node); transaction.deleteNode(node);
} }
} }
@ -92,7 +97,7 @@ extension SelectionTransform on EditorState {
// All other nodes can be deleted. // All other nodes can be deleted.
if (i != 0) { if (i != 0) {
// Never delete a table cell node child // Never delete a table cell node child
if (node.parent?.type == TableCellBlockKeys.type) { if (ignoreNodeTypes.contains(node.parent?.type)) {
if (!nodes.any((n) => n.id == node.parent?.parent?.id)) { if (!nodes.any((n) => n.id == node.parent?.parent?.id)) {
transaction.deleteText( transaction.deleteText(
node, node,
@ -105,13 +110,13 @@ extension SelectionTransform on EditorState {
// node, So we should not delete the last node. Just delete part of // node, So we should not delete the last node. Just delete part of
// the text inside selection // the text inside selection
else if (node.id == nodes.last.id && else if (node.id == nodes.last.id &&
nodes.first.parent?.type == TableCellBlockKeys.type) { ignoreNodeTypes.contains(nodes.first.parent?.type)) {
transaction.deleteText( transaction.deleteText(
node, node,
0, 0,
selection.end.offset, selection.end.offset,
); );
} else if (node.type != TableCellBlockKeys.type) { } else if (!ignoreNodeTypes.contains(node.type)) {
transaction.deleteNode(node); transaction.deleteNode(node);
} }
continue; continue;
@ -122,7 +127,7 @@ extension SelectionTransform on EditorState {
// the text between the two nodes. // the text between the two nodes.
if (nodes.last.delta != null && if (nodes.last.delta != null &&
![node.parent?.type, nodes.last.parent?.type] ![node.parent?.type, nodes.last.parent?.type]
.contains(TableCellBlockKeys.type)) { .any((type) => ignoreNodeTypes.contains(type))) {
transaction.mergeText( transaction.mergeText(
node, node,
nodes.last, nodes.last,
@ -154,8 +159,8 @@ extension SelectionTransform on EditorState {
else { else {
// If the last or first node is inside table we will only delete // If the last or first node is inside table we will only delete
// selection part of first node. // selection part of first node.
if (nodes.last.parent?.type == TableCellBlockKeys.type || if (ignoreNodeTypes.contains(nodes.last.parent?.type) ||
node.parent?.type == TableCellBlockKeys.type) { ignoreNodeTypes.contains(node.parent?.type)) {
transaction.deleteText( transaction.deleteText(
node, node,
selection.startIndex, selection.startIndex,

View file

@ -21,17 +21,18 @@ extension EditorCopyPaste on EditorState {
final insertedDelta = insertedNode.delta; final insertedDelta = insertedNode.delta;
// if the node is empty paragraph (default), replace it with the inserted node. // if the node is empty paragraph (default), replace it with the inserted node.
if (delta.isEmpty && node.type == ParagraphBlockKeys.type) { if (delta.isEmpty && node.type == ParagraphBlockKeys.type) {
transaction.insertNode( final List<Node> combinedChildren = [
selection.end.path.next, ...insertedNode.children.map((e) => e.deepCopy()),
insertedNode, // if the original node has children, copy them to the inserted node.
); ...node.children.map((e) => e.deepCopy()),
];
insertedNode = insertedNode.copyWith(children: combinedChildren);
transaction.insertNode(selection.end.path, insertedNode);
transaction.deleteNode(node); transaction.deleteNode(node);
final path = calculatePath(selection.end.path, [insertedNode]);
final offset = calculateLength([insertedNode]);
transaction.afterSelection = Selection.collapsed( transaction.afterSelection = Selection.collapsed(
Position( Position(
path: path, path: selection.end.path,
offset: offset, offset: insertedDelta?.length ?? 0,
), ),
); );
} else if (insertedDelta != null) { } else if (insertedDelta != null) {

View file

@ -92,8 +92,10 @@ enum TransactionTime {
class EditorState { class EditorState {
EditorState({ EditorState({
required this.document, required this.document,
this.minHistoryItemDuration = const Duration(milliseconds: 200), this.minHistoryItemDuration = const Duration(milliseconds: 50),
int? maxHistoryItemSize,
}) { }) {
undoManager = UndoManager(maxHistoryItemSize ?? 200);
undoManager.state = this; undoManager.state = this;
} }
@ -252,7 +254,7 @@ class EditorState {
_sliceUpcomingAttributes = value; _sliceUpcomingAttributes = value;
} }
final UndoManager undoManager = UndoManager(); late final UndoManager undoManager;
Transaction get transaction { Transaction get transaction {
final transaction = Transaction(document: document); final transaction = Transaction(document: document);