Skip to content

Commit f7b0b59

Browse files
authored
Add .moveNode() to MutableDocument & DocumentEditorTransaction (#634)
1 parent d554e13 commit f7b0b59

File tree

2 files changed

+123
-0
lines changed

2 files changed

+123
-0
lines changed

super_editor/lib/src/core/document_editor.dart

+24
Original file line numberDiff line numberDiff line change
@@ -100,6 +100,14 @@ class DocumentEditorTransaction {
100100
_document.deleteNodeAt(index);
101101
}
102102

103+
/// Moves a [DocumentNode] matching the given [nodeId] from its current index
104+
/// in the [Document] to the given [targetIndex].
105+
///
106+
/// If none of the nodes in this document match [nodeId], throws an error.
107+
void moveNode({required String nodeId, required int targetIndex}) {
108+
_document.moveNode(nodeId: nodeId, targetIndex: targetIndex);
109+
}
110+
103111
/// Replaces the given [oldNode] with the given [newNode]
104112
void replaceNode({
105113
required DocumentNode oldNode,
@@ -273,6 +281,22 @@ class MutableDocument with ChangeNotifier implements Document {
273281
return isRemoved;
274282
}
275283

284+
/// Moves a [DocumentNode] matching the given [nodeId] from its current index
285+
/// in the [Document] to the given [targetIndex].
286+
///
287+
/// If none of the nodes in this document match [nodeId], throws an error.
288+
void moveNode({required String nodeId, required int targetIndex}) {
289+
final node = getNodeById(nodeId);
290+
if (node == null) {
291+
throw Exception('Could not find node with nodeId: $nodeId');
292+
}
293+
294+
if (nodes.remove(node)) {
295+
nodes.insert(targetIndex, node);
296+
notifyListeners();
297+
}
298+
}
299+
276300
/// Replaces the given [oldNode] with the given [newNode]
277301
void replaceNode({
278302
required DocumentNode oldNode,

super_editor/test/src/core/document_editor_test.dart

+99
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,105 @@ import 'package:super_editor/super_editor.dart';
33

44
void main() {
55
group('MutableDocument', () {
6+
group('.moveNode()', () {
7+
test('when the document is empty, throws an exception', () {
8+
final document = MutableDocument(nodes: []);
9+
expect(
10+
() => document.moveNode(nodeId: 'does-not-exist', targetIndex: 0),
11+
throwsException,
12+
);
13+
});
14+
15+
test('when the node does not exist in the document, throws an exception', () {
16+
final node = ParagraphNode(id: 'move-me', text: AttributedText());
17+
final document = MutableDocument(
18+
nodes: [
19+
HorizontalRuleNode(id: '0'),
20+
node,
21+
HorizontalRuleNode(id: '2'),
22+
],
23+
);
24+
25+
expect(
26+
() => document.moveNode(nodeId: 'does-not-exist', targetIndex: 0),
27+
throwsException,
28+
);
29+
});
30+
31+
test('when the given target index is negative, throws a RangeError', () {
32+
final node = ParagraphNode(id: 'move-me', text: AttributedText());
33+
final document = MutableDocument(
34+
nodes: [
35+
HorizontalRuleNode(id: '0'),
36+
node,
37+
HorizontalRuleNode(id: '2'),
38+
],
39+
);
40+
41+
expect(
42+
() => document.moveNode(nodeId: 'move-me', targetIndex: -1),
43+
throwsRangeError,
44+
);
45+
});
46+
47+
test('when the given target index is out of document bounds, throws a RangeError', () {
48+
final node = ParagraphNode(id: 'move-me', text: AttributedText());
49+
final document = MutableDocument(
50+
nodes: [
51+
HorizontalRuleNode(id: '0'),
52+
node,
53+
HorizontalRuleNode(id: '2'),
54+
],
55+
);
56+
57+
expect(
58+
() => document.moveNode(nodeId: 'move-me', targetIndex: 3),
59+
throwsRangeError,
60+
);
61+
});
62+
63+
test('when the node exists in the document, and the targetIndex is valid, moves it to the given target index', () {
64+
final node = ParagraphNode(id: 'move-me', text: AttributedText());
65+
final document = MutableDocument(
66+
nodes: [
67+
HorizontalRuleNode(id: '0'),
68+
node,
69+
HorizontalRuleNode(id: '2'),
70+
],
71+
);
72+
73+
document.moveNode(nodeId: 'move-me', targetIndex: 0);
74+
expect(
75+
document.nodes,
76+
[
77+
node, // Node exists at index 0
78+
HorizontalRuleNode(id: '0'),
79+
HorizontalRuleNode(id: '2'),
80+
],
81+
);
82+
83+
document.moveNode(nodeId: 'move-me', targetIndex: 2);
84+
expect(
85+
document.nodes,
86+
[
87+
HorizontalRuleNode(id: '0'),
88+
HorizontalRuleNode(id: '2'),
89+
node, // Node exists at index 2
90+
],
91+
);
92+
93+
document.moveNode(nodeId: 'move-me', targetIndex: 1);
94+
expect(
95+
document.nodes,
96+
[
97+
HorizontalRuleNode(id: '0'),
98+
node, // Node exists at index 1
99+
HorizontalRuleNode(id: '2'),
100+
],
101+
);
102+
});
103+
});
104+
6105
test('it replaces one node by another ', () {
7106
final oldNode = ParagraphNode(id: 'old', text: AttributedText());
8107
final document = MutableDocument(

0 commit comments

Comments
 (0)