mirror of
https://github.com/AppFlowy-IO/AppFlowy.git
synced 2024-08-30 18:12:39 +00:00
Merge branch 'AppFlowy-IO:feat/flowy_editor' into feat/flowy_editor
This commit is contained in:
@ -4,18 +4,21 @@
|
|||||||
"children": [
|
"children": [
|
||||||
{
|
{
|
||||||
"type": "text",
|
"type": "text",
|
||||||
|
"delta": [],
|
||||||
"attributes": {
|
"attributes": {
|
||||||
"subtype": "with-heading"
|
"subtype": "with-heading"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"type": "text",
|
"type": "text",
|
||||||
|
"delta": [],
|
||||||
"attributes": {
|
"attributes": {
|
||||||
"tag": "*"
|
"tag": "*"
|
||||||
},
|
},
|
||||||
"children": [
|
"children": [
|
||||||
{
|
{
|
||||||
"type": "text",
|
"type": "text",
|
||||||
|
"delta": [],
|
||||||
"attributes": {
|
"attributes": {
|
||||||
"text-type": "heading2",
|
"text-type": "heading2",
|
||||||
"check": true
|
"check": true
|
||||||
@ -23,6 +26,7 @@
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
"type": "text",
|
"type": "text",
|
||||||
|
"delta": [],
|
||||||
"attributes": {
|
"attributes": {
|
||||||
"text-type": "checkbox",
|
"text-type": "checkbox",
|
||||||
"check": true
|
"check": true
|
||||||
@ -30,6 +34,7 @@
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
"type": "text",
|
"type": "text",
|
||||||
|
"delta": [],
|
||||||
"attributes": {
|
"attributes": {
|
||||||
"tag": "**"
|
"tag": "**"
|
||||||
}
|
}
|
||||||
|
@ -176,4 +176,11 @@ class TextNode extends Node {
|
|||||||
_delta = v;
|
_delta = v;
|
||||||
notifyListeners();
|
notifyListeners();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Map<String, Object> toJson() {
|
||||||
|
final map = super.toJson();
|
||||||
|
map['delta'] = _delta.toJson();
|
||||||
|
return map;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -40,7 +40,7 @@ class StateTree {
|
|||||||
if (path.isEmpty) {
|
if (path.isEmpty) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
var node = root.childAtPath(path);
|
final node = root.childAtPath(path);
|
||||||
if (node == null || node is! TextNode) {
|
if (node == null || node is! TextNode) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
import 'dart:collection';
|
import 'dart:collection';
|
||||||
import 'dart:math';
|
import 'dart:math';
|
||||||
|
|
||||||
|
import 'package:flowy_editor/document/attributes.dart';
|
||||||
import 'package:flutter/foundation.dart';
|
import 'package:flutter/foundation.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import './attributes.dart';
|
import './attributes.dart';
|
||||||
@ -8,18 +9,14 @@ import './attributes.dart';
|
|||||||
// constant number: 2^53 - 1
|
// constant number: 2^53 - 1
|
||||||
const int _maxInt = 9007199254740991;
|
const int _maxInt = 9007199254740991;
|
||||||
|
|
||||||
class TextOperation {
|
abstract class TextOperation {
|
||||||
bool get isEmpty {
|
bool get isEmpty => length == 0;
|
||||||
return length == 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
int get length {
|
int get length;
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
Attributes? get attributes {
|
Attributes? get attributes => null;
|
||||||
return null;
|
|
||||||
}
|
Map<String, dynamic> toJson();
|
||||||
}
|
}
|
||||||
|
|
||||||
class TextInsert extends TextOperation {
|
class TextInsert extends TextOperation {
|
||||||
@ -54,6 +51,18 @@ class TextInsert extends TextOperation {
|
|||||||
return Object.hash(
|
return Object.hash(
|
||||||
contentHash, attrs == null ? null : hashAttributes(attrs));
|
contentHash, attrs == null ? null : hashAttributes(attrs));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Map<String, dynamic> toJson() {
|
||||||
|
final result = <String, dynamic>{
|
||||||
|
'insert': content,
|
||||||
|
};
|
||||||
|
final attrs = _attributes;
|
||||||
|
if (attrs != null) {
|
||||||
|
result['attributes'] = {...attrs};
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class TextRetain extends TextOperation {
|
class TextRetain extends TextOperation {
|
||||||
@ -96,6 +105,18 @@ class TextRetain extends TextOperation {
|
|||||||
final attrs = _attributes;
|
final attrs = _attributes;
|
||||||
return Object.hash(_length, attrs == null ? null : hashAttributes(attrs));
|
return Object.hash(_length, attrs == null ? null : hashAttributes(attrs));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Map<String, dynamic> toJson() {
|
||||||
|
final result = <String, dynamic>{
|
||||||
|
'retain': _length,
|
||||||
|
};
|
||||||
|
final attrs = _attributes;
|
||||||
|
if (attrs != null) {
|
||||||
|
result['attributes'] = {...attrs};
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class TextDelete extends TextOperation {
|
class TextDelete extends TextOperation {
|
||||||
@ -103,11 +124,6 @@ class TextDelete extends TextOperation {
|
|||||||
|
|
||||||
TextDelete(int length) : _length = length;
|
TextDelete(int length) : _length = length;
|
||||||
|
|
||||||
@override
|
|
||||||
bool get isEmpty {
|
|
||||||
return length == 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
@override
|
||||||
int get length {
|
int get length {
|
||||||
return _length;
|
return _length;
|
||||||
@ -129,6 +145,13 @@ class TextDelete extends TextOperation {
|
|||||||
int get hashCode {
|
int get hashCode {
|
||||||
return _length.hashCode;
|
return _length.hashCode;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Map<String, dynamic> toJson() {
|
||||||
|
return {
|
||||||
|
'delete': _length,
|
||||||
|
};
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class _OpIterator {
|
class _OpIterator {
|
||||||
@ -215,28 +238,17 @@ class _OpIterator {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Attributes? _attributesFromJSON(Map<String, dynamic>? json) {
|
|
||||||
if (json == null) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
final result = <String, dynamic>{};
|
|
||||||
|
|
||||||
for (final entry in json.entries) {
|
|
||||||
result[entry.key] = entry.value;
|
|
||||||
}
|
|
||||||
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
TextOperation? _textOperationFromJson(Map<String, dynamic> json) {
|
TextOperation? _textOperationFromJson(Map<String, dynamic> json) {
|
||||||
TextOperation? result;
|
TextOperation? result;
|
||||||
|
|
||||||
if (json['insert'] is String) {
|
if (json['insert'] is String) {
|
||||||
result = TextInsert(json['insert'] as String,
|
final attrs = json['attributes'] as Map<String, dynamic>?;
|
||||||
_attributesFromJSON(json['attributes'] as Map<String, dynamic>?));
|
result =
|
||||||
|
TextInsert(json['insert'] as String, attrs == null ? null : {...attrs});
|
||||||
} else if (json['retain'] is int) {
|
} else if (json['retain'] is int) {
|
||||||
result = TextRetain(json['retain'] as int,
|
final attrs = json['attributes'] as Map<String, dynamic>?;
|
||||||
_attributesFromJSON(json['attributes'] as Map<String, Object>?));
|
result =
|
||||||
|
TextRetain(json['retain'] as int, attrs == null ? null : {...attrs});
|
||||||
} else if (json['delete'] is int) {
|
} else if (json['delete'] is int) {
|
||||||
result = TextDelete(json['delete'] as int);
|
result = TextDelete(json['delete'] as int);
|
||||||
}
|
}
|
||||||
@ -459,4 +471,8 @@ class Delta {
|
|||||||
});
|
});
|
||||||
return inverted.chop();
|
return inverted.chop();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
List<dynamic> toJson() {
|
||||||
|
return operations.map((e) => e.toJson()).toList();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -197,4 +197,37 @@ void main() {
|
|||||||
// expect(base.compose(delta).compose(inverted), base);
|
// expect(base.compose(delta).compose(inverted), base);
|
||||||
// });
|
// });
|
||||||
});
|
});
|
||||||
|
group('json', () {
|
||||||
|
test('toJson()', () {
|
||||||
|
final delta = Delta().retain(2).insert('A').delete(3);
|
||||||
|
expect(delta.toJson(), [
|
||||||
|
{'retain': 2},
|
||||||
|
{'insert': 'A'},
|
||||||
|
{'delete': 3}
|
||||||
|
]);
|
||||||
|
});
|
||||||
|
test('attributes', () {
|
||||||
|
final delta =
|
||||||
|
Delta().retain(2, {'bold': true}).insert('A', {'italic': true});
|
||||||
|
expect(delta.toJson(), [
|
||||||
|
{
|
||||||
|
'retain': 2,
|
||||||
|
'attributes': {'bold': true},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
'insert': 'A',
|
||||||
|
'attributes': {'italic': true},
|
||||||
|
},
|
||||||
|
]);
|
||||||
|
});
|
||||||
|
test('fromJson()', () {
|
||||||
|
final delta = Delta.fromJson([
|
||||||
|
{'retain': 2},
|
||||||
|
{'insert': 'A'},
|
||||||
|
{'delete': 3},
|
||||||
|
]);
|
||||||
|
final expected = Delta().retain(2).insert('A').delete(3);
|
||||||
|
expect(delta, expected);
|
||||||
|
});
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
Reference in New Issue
Block a user