mirror of
https://github.com/AppFlowy-IO/AppFlowy.git
synced 2024-08-30 18:12:39 +00:00
feat: insert / delelte / update / search node in state tree
This commit is contained in:
parent
47436bf6e2
commit
59d92a8ced
@ -40,6 +40,7 @@
|
||||
"attributes": {
|
||||
"url": "x.mp4"
|
||||
}
|
||||
|
||||
}
|
||||
]
|
||||
}
|
||||
|
@ -1,5 +1,4 @@
|
||||
import 'dart:collection';
|
||||
|
||||
import 'package:flowy_editor/document/path.dart';
|
||||
|
||||
class Node extends LinkedListEntry<Node> {
|
||||
@ -35,11 +34,23 @@ class Node extends LinkedListEntry<Node> {
|
||||
);
|
||||
}
|
||||
|
||||
return Node(
|
||||
final node = Node(
|
||||
type: jType,
|
||||
children: children,
|
||||
attributes: jAttributes,
|
||||
);
|
||||
|
||||
for (final child in children) {
|
||||
child.parent = node;
|
||||
}
|
||||
|
||||
return node;
|
||||
}
|
||||
|
||||
void updateAttributes(Map<String, Object> attributes) {
|
||||
for (final attribute in attributes.entries) {
|
||||
this.attributes[attribute.key] = attribute.value;
|
||||
}
|
||||
}
|
||||
|
||||
Node? childAtIndex(int index) {
|
||||
@ -58,6 +69,24 @@ class Node extends LinkedListEntry<Node> {
|
||||
return childAtIndex(path.first)?.childAtPath(path.sublist(1));
|
||||
}
|
||||
|
||||
@override
|
||||
void insertAfter(Node entry) {
|
||||
entry.parent = parent;
|
||||
super.insertAfter(entry);
|
||||
}
|
||||
|
||||
@override
|
||||
void insertBefore(Node entry) {
|
||||
entry.parent = parent;
|
||||
super.insertBefore(entry);
|
||||
}
|
||||
|
||||
@override
|
||||
void unlink() {
|
||||
parent = null;
|
||||
super.unlink();
|
||||
}
|
||||
|
||||
Map<String, Object> toJson() {
|
||||
var map = <String, Object>{
|
||||
'type': type,
|
||||
|
@ -1,7 +1,8 @@
|
||||
import 'package:flowy_editor/document/node.dart';
|
||||
import 'package:flowy_editor/document/path.dart';
|
||||
|
||||
class StateTree {
|
||||
Node root;
|
||||
final Node root;
|
||||
|
||||
StateTree({required this.root});
|
||||
|
||||
@ -13,8 +14,43 @@ class StateTree {
|
||||
return StateTree(root: root);
|
||||
}
|
||||
|
||||
// bool insert(Path path, Node node) {
|
||||
// final insertedNode = root
|
||||
// return false;
|
||||
// }
|
||||
Node? nodeAtPath(Path path) {
|
||||
return root.childAtPath(path);
|
||||
}
|
||||
|
||||
bool insert(Path path, Node node) {
|
||||
if (path.isEmpty) {
|
||||
return false;
|
||||
}
|
||||
final insertedNode = root.childAtPath(
|
||||
path.sublist(0, path.length - 1) + [path.last - 1],
|
||||
);
|
||||
if (insertedNode == null) {
|
||||
return false;
|
||||
}
|
||||
insertedNode.insertAfter(node);
|
||||
return true;
|
||||
}
|
||||
|
||||
Node? delete(Path path) {
|
||||
if (path.isEmpty) {
|
||||
return null;
|
||||
}
|
||||
final deletedNode = root.childAtPath(path);
|
||||
deletedNode?.unlink();
|
||||
return deletedNode;
|
||||
}
|
||||
|
||||
Map<String, Object>? update(Path path, Map<String, Object> attributes) {
|
||||
if (path.isEmpty) {
|
||||
return null;
|
||||
}
|
||||
final updatedNode = root.childAtPath(path);
|
||||
if (updatedNode == null) {
|
||||
return null;
|
||||
}
|
||||
final previousAttributes = {...updatedNode.attributes};
|
||||
updatedNode.updateAttributes(attributes);
|
||||
return previousAttributes;
|
||||
}
|
||||
}
|
||||
|
@ -1,5 +1,6 @@
|
||||
import 'dart:convert';
|
||||
|
||||
import 'package:flowy_editor/document/node.dart';
|
||||
import 'package:flowy_editor/document/state_tree.dart';
|
||||
import 'package:flutter/services.dart';
|
||||
import 'package:flutter_test/flutter_test.dart';
|
||||
@ -13,11 +14,48 @@ void main() {
|
||||
final stateTree = StateTree.fromJson(data);
|
||||
expect(stateTree.root.type, 'root');
|
||||
expect(stateTree.root.toJson(), data['document']);
|
||||
expect(stateTree.root.children.last.type, 'video');
|
||||
});
|
||||
|
||||
test('search node in state tree', () async {
|
||||
final String response = await rootBundle.loadString('assets/document.json');
|
||||
final data = Map<String, Object>.from(json.decode(response));
|
||||
final stateTree = StateTree.fromJson(data);
|
||||
final checkBoxNode = stateTree.root.childAtPath([1, 0]);
|
||||
expect(checkBoxNode != null, true);
|
||||
final textType = checkBoxNode!.attributes['text-type'];
|
||||
expect(textType != null, true);
|
||||
});
|
||||
|
||||
test('insert node in state tree', () async {
|
||||
final String response = await rootBundle.loadString('assets/document.json');
|
||||
final data = Map<String, Object>.from(json.decode(response));
|
||||
final stateTree = StateTree.fromJson(data);
|
||||
final insertNode = Node.fromJson({
|
||||
'type': 'text',
|
||||
});
|
||||
bool result = stateTree.insert([1, 1], insertNode);
|
||||
expect(result, true);
|
||||
expect(identical(insertNode, stateTree.nodeAtPath([1, 1])), true);
|
||||
});
|
||||
|
||||
test('delete node in state tree', () async {
|
||||
final String response = await rootBundle.loadString('assets/document.json');
|
||||
final data = Map<String, Object>.from(json.decode(response));
|
||||
final stateTree = StateTree.fromJson(data);
|
||||
final deletedNode = stateTree.delete([1, 0]);
|
||||
expect(deletedNode != null, true);
|
||||
expect(deletedNode!.attributes['text-type'], 'check-box');
|
||||
});
|
||||
|
||||
test('update node in state tree', () async {
|
||||
final String response = await rootBundle.loadString('assets/document.json');
|
||||
final data = Map<String, Object>.from(json.decode(response));
|
||||
final stateTree = StateTree.fromJson(data);
|
||||
final attributes = stateTree.update([1, 0], {'text-type': 'heading1'});
|
||||
expect(attributes != null, true);
|
||||
expect(attributes!['text-type'], 'check-box');
|
||||
final updatedNode = stateTree.nodeAtPath([1, 0]);
|
||||
expect(updatedNode != null, true);
|
||||
expect(updatedNode!.attributes['text-type'], 'heading1');
|
||||
});
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user