feat: rename delta and document (#153)

* chore: rename delta

* fix: rename attributes to data

* chore: rename document to page

* fix: flutter analyze
This commit is contained in:
Lucas.Xu 2023-06-01 14:43:12 +08:00 • committed by GitHub
commit c573b9a80f
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
26 changed files with 243 additions and 247 deletions

View file

@ -169,7 +169,7 @@ We'll use `network_image` in this case. And we add `network_image_src` to the `a
```JSON
{
"type": "network_image",
"attributes": {
"data": {
"network_image_src": "https://docs.flutter.dev/assets/images/dash/dash-fainting.gif"
}
}

View file

@ -1,16 +1,16 @@
{
"document": {
"type": "document",
"type": "page",
"children": [{
"type": "heading",
"attributes": {
"data": {
"level": 2,
"delta": [{
"insert": "👋 "
},
{
"insert": "Welcome to",
"attributes": {
"data": {
"bold": true
}
},
@ -19,7 +19,7 @@
},
{
"insert": "AppFlowy Editor",
"attributes": {
"data": {
"href": "appflowy.io",
"italic": true,
"bold": true
@ -30,13 +30,13 @@
},
{
"type": "paragraph",
"attributes": {
"data": {
"delta": []
}
},
{
"type": "paragraph",
"attributes": {
"data": {
"delta": [{
"insert": "AppFlowy Editor is a"
},
@ -45,7 +45,7 @@
},
{
"insert": "highly customizable",
"attributes": {
"data": {
"bold": true
}
},
@ -54,7 +54,7 @@
},
{
"insert": "rich-text editor",
"attributes": {
"data": {
"italic": true
}
},
@ -63,7 +63,7 @@
},
{
"insert": "Flutter",
"attributes": {
"data": {
"underline": true
}
}
@ -72,7 +72,7 @@
},
{
"type": "todo_list",
"attributes": {
"data": {
"checked": true,
"delta": [{
"insert": "Customizable"
@ -81,7 +81,7 @@
},
{
"type": "todo_list",
"attributes": {
"data": {
"checked": true,
"delta": [{
"insert": "Test-covered"
@ -90,7 +90,7 @@
},
{
"type": "todo_list",
"attributes": {
"data": {
"checked": false,
"delta": [{
"insert": "more to come!"
@ -99,13 +99,13 @@
},
{
"type": "paragraph",
"attributes": {
"data": {
"delta": []
}
},
{
"type": "quote",
"attributes": {
"data": {
"delta": [{
"insert": "Here is an example you can give a try"
}]
@ -113,19 +113,19 @@
},
{
"type": "paragraph",
"attributes": {
"data": {
"delta": []
}
},
{
"type": "paragraph",
"attributes": {
"data": {
"delta": [{
"insert": "You can also use "
},
{
"insert": "AppFlowy Editor",
"attributes": {
"data": {
"italic": true,
"bold": true,
"textColor": "0xffD70040",
@ -140,13 +140,13 @@
},
{
"type": "paragraph",
"attributes": {
"data": {
"delta": []
}
},
{
"type": "bulleted_list",
"attributes": {
"data": {
"delta": [{
"insert": "Use / to insert blocks"
}]
@ -155,7 +155,7 @@
},
{
"type": "bulleted_list",
"attributes": {
"data": {
"delta": [{
"insert": "Select text to trigger to the toolbar to format your notes."
}]
@ -164,13 +164,13 @@
},
{
"type": "paragraph",
"attributes": {
"data": {
"delta": []
}
},
{
"type": "paragraph",
"attributes": {
"data": {
"delta": [{
"insert": "If you have questions or feedback, please submit an issue on Github or join the community along with 1000+ builders!"
}]

View file

@ -205,6 +205,7 @@
files = (
);
inputPaths = (
"${TARGET_BUILD_DIR}/${INFOPLIST_PATH}",
);
name = "Thin Binary";
outputPaths = (

View file

@ -29,6 +29,7 @@ export 'src/service/shortcut_event/shortcut_event.dart';
export 'src/service/shortcut_event/shortcut_event_handler.dart';
export 'src/render/rich_text/default_selectable.dart';
export 'src/render/rich_text/flowy_rich_text.dart';
export 'src/render/rich_text/flowy_rich_text_keys.dart';
export 'src/render/selection_menu/selection_menu_widget.dart';
export 'src/render/selection_menu/selection_menu_item_widget.dart';
export 'src/l10n/l10n.dart';

View file

@ -34,7 +34,7 @@ class Document {
factory Document.blank({bool withInitialText = false}) {
final root = Node(
type: 'document',
type: 'page',
children: withInitialText ? [paragraphNode()] : [],
);
return Document(

View file

@ -8,7 +8,7 @@ import 'package:nanoid/nanoid.dart';
///
/// It contains three parts:
/// - [type]: The type of the node to determine which block component to render it.
/// - [attributes]: The attributes of the node to determine how to render it.
/// - [data]: The data of the node to determine how to render it.
/// - [children]: The children of the node.
///
///
@ -41,7 +41,7 @@ final class Node extends ChangeNotifier with LinkedListEntry<Node> {
factory Node.fromJson(Map<String, Object> json) {
final node = Node(
type: json['type'] as String,
attributes: Attributes.from(json['attributes'] as Map? ?? {}),
attributes: Attributes.from(json['data'] as Map? ?? {}),
children: (json['children'] as List? ?? [])
.map((e) => Map<String, Object>.from(e))
.map((e) => Node.fromJson(e)),
@ -195,7 +195,7 @@ final class Node extends ChangeNotifier with LinkedListEntry<Node> {
.toList(growable: false);
}
if (attributes.isNotEmpty) {
map['attributes'] = attributes;
map['data'] = attributes;
}
return map;
}

View file

@ -1,5 +1,5 @@
// entry
export 'entry/document_component.dart';
export 'entry/page_block_component.dart';
// ime
export 'service/ime/delta_input_service.dart';

View file

@ -0,0 +1,51 @@
import 'package:appflowy_editor/appflowy_editor.dart';
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
class PageBlockKeys {
const PageBlockKeys._();
static const String type = 'page';
}
Node pageNode({
required Iterable<Node> children,
Attributes attributes = const {},
}) {
return Node(
type: PageBlockKeys.type,
children: children,
attributes: attributes,
);
}
class PageBlockComponentBuilder extends BlockComponentBuilder {
@override
BlockComponentWidget build(BlockComponentContext blockComponentContext) {
return PageBlockComponent(
key: blockComponentContext.node.key,
node: blockComponentContext.node,
);
}
}
class PageBlockComponent extends BlockComponentStatelessWidget {
const PageBlockComponent({
super.key,
required super.node,
super.showActions,
super.actionBuilder,
super.configuration = const BlockComponentConfiguration(),
});
@override
Widget build(BuildContext context) {
final editorState = Provider.of<EditorState>(context, listen: false);
final children = editorState.renderer.buildList(context, node.children);
return Column(
crossAxisAlignment: CrossAxisAlignment.start,
mainAxisSize: MainAxisSize.min,
children: children,
);
}
}

View file

@ -49,13 +49,17 @@ void showColorMenu(
}
void _formatHighlightColor(EditorState editorState, String color) {
final selection = editorState.selection!;
editorState.formatDelta(selection, {'highlightColor': color});
editorState.formatDelta(
editorState.selection,
{FlowyRichTextKeys.highlightColor: color},
);
}
void _formatFontColor(EditorState editorState, String color) {
final selection = editorState.selection!;
editorState.formatDelta(selection, {'textColor': color});
editorState.formatDelta(
editorState.selection,
{FlowyRichTextKeys.textColor: color},
);
}
List<ColorOption> _generateTextColorOptions(EditorState editorState) {

View file

@ -11,10 +11,7 @@ final highlightColorItem = ToolbarItem(
final nodes = editorState.getNodesInSelection(selection);
final isHighlight = nodes.allSatisfyInSelection(selection, (delta) {
return delta.everyAttributes(
(attributes) {
highlightColorHex = attributes['highlightColor'];
return (highlightColorHex != null);
},
(attributes) => attributes[FlowyRichTextKeys.highlightColor] != null,
);
});
return IconItemWidget(

View file

@ -10,10 +10,7 @@ final textColorItem = ToolbarItem(
final nodes = editorState.getNodesInSelection(selection);
final isHighlight = nodes.allSatisfyInSelection(selection, (delta) {
return delta.everyAttributes(
(attributes) {
textColorHex = attributes['textColor'];
return (textColorHex != null);
},
(attributes) => attributes[FlowyRichTextKeys.textColor] != null,
);
});
return IconItemWidget(

View file

@ -1,5 +1,4 @@
import 'package:appflowy_editor/appflowy_editor.dart';
import 'package:flutter/material.dart';
extension NodeAttributesExtensions on Attributes {
String? get heading {
@ -24,14 +23,6 @@ extension NodeAttributesExtensions on Attributes {
return null;
}
bool get code {
if (containsKey(BuiltInAttributeKey.code) &&
this[BuiltInAttributeKey.code] is bool) {
return this[BuiltInAttributeKey.code];
}
return false;
}
bool get check {
if (containsKey(BuiltInAttributeKey.checkbox) &&
this[BuiltInAttributeKey.checkbox] is bool) {
@ -40,51 +31,3 @@ extension NodeAttributesExtensions on Attributes {
return false;
}
}
extension DeltaAttributesExtensions on Attributes {
bool get bold {
return (containsKey(BuiltInAttributeKey.bold) &&
this[BuiltInAttributeKey.bold] == true);
}
bool get italic {
return (containsKey(BuiltInAttributeKey.italic) &&
this[BuiltInAttributeKey.italic] == true);
}
bool get underline {
return (containsKey(BuiltInAttributeKey.underline) &&
this[BuiltInAttributeKey.underline] == true);
}
bool get strikethrough {
return (containsKey(BuiltInAttributeKey.strikethrough) &&
this[BuiltInAttributeKey.strikethrough] == true);
}
static const whiteInt = 0XFFFFFFFF;
Color? get color {
final textColor = this[BuiltInAttributeKey.textColor] as String?;
if (textColor != null) {
return textColor.toColor();
}
return null;
}
Color? get backgroundColor {
final highlightColor = this[BuiltInAttributeKey.highlightColor] as String?;
if (highlightColor != null) {
return highlightColor.toColor();
}
return null;
}
String? get href {
if (containsKey(BuiltInAttributeKey.href) &&
this[BuiltInAttributeKey.href] is String) {
return this[BuiltInAttributeKey.href];
}
return null;
}
}

View file

@ -60,7 +60,7 @@ class HTMLToNodesConverter {
final childNodes = _document.body?.nodes.toList() ?? <html.Node>[];
return Document.fromJson({
'document': {
'type': 'document',
'type': 'page',
'children': _handleContainer(childNodes).map((e) => e.toJson()).toList()
}
});

View file

@ -78,19 +78,19 @@ class DocumentHTMLDecoder extends Converter<String, Document> {
Attributes? attributes;
switch (localName) {
case HTMLTags.bold || HTMLTags.strong:
attributes = {'bold': true};
attributes = {FlowyRichTextKeys.bold: true};
break;
case HTMLTags.italic || HTMLTags.em:
attributes = {'italic': true};
attributes = {FlowyRichTextKeys.italic: true};
break;
case HTMLTags.underline:
attributes = {'underline': true};
attributes = {FlowyRichTextKeys.underline: true};
break;
case HTMLTags.del:
attributes = {'strikethrough': true};
attributes = {FlowyRichTextKeys.strikethrough: true};
break;
case HTMLTags.code:
attributes = {'code': true};
attributes = {FlowyRichTextKeys.code: true};
case HTMLTags.span:
attributes = _getDeltaAttributesFromHTMLAttributes(
element.attributes,
@ -100,7 +100,7 @@ class DocumentHTMLDecoder extends Converter<String, Document> {
final href = element.attributes['href'];
if (href != null) {
attributes = {
'href': href,
FlowyRichTextKeys.href: href,
};
}
break;
@ -180,11 +180,11 @@ class DocumentHTMLDecoder extends Converter<String, Document> {
final fontWeight = css['font-weight'];
if (fontWeight != null) {
if (fontWeight == 'bold') {
attributes['bold'] = true;
attributes[FlowyRichTextKeys.bold] = true;
} else {
final weight = int.tryParse(fontWeight);
if (weight != null && weight >= 500) {
attributes['bold'] = true;
attributes[FlowyRichTextKeys.bold] = true;
}
}
}
@ -196,10 +196,10 @@ class DocumentHTMLDecoder extends Converter<String, Document> {
for (final decoration in decorations) {
switch (decoration) {
case 'underline':
attributes['underline'] = true;
attributes[FlowyRichTextKeys.underline] = true;
break;
case 'line-through':
attributes['strike'] = true;
attributes[FlowyRichTextKeys.strikethrough] = true;
break;
default:
break;
@ -212,14 +212,14 @@ class DocumentHTMLDecoder extends Converter<String, Document> {
if (backgroundColor != null) {
final highlightColor = backgroundColor.tryToColor()?.toHex();
if (highlightColor != null) {
attributes['highlightColor'] = highlightColor;
attributes[FlowyRichTextKeys.highlightColor] = highlightColor;
}
}
// italic
final fontStyle = css['font-style'];
if (fontStyle == 'italic') {
attributes['italic'] = true;
attributes[FlowyRichTextKeys.italic] = true;
}
return attributes.isEmpty ? null : attributes;

View file

@ -1,12 +1,23 @@
import 'dart:async';
import 'dart:ui';
import 'package:appflowy_editor/src/core/document/attributes.dart';
import 'package:appflowy_editor/src/core/document/node.dart';
import 'package:appflowy_editor/src/core/document/text_delta.dart';
import 'package:appflowy_editor/src/core/location/position.dart';
import 'package:appflowy_editor/src/core/location/selection.dart';
import 'package:appflowy_editor/src/editor/toolbar/toolbar.dart';
import 'package:appflowy_editor/src/editor_state.dart';
import 'package:appflowy_editor/src/extensions/url_launcher_extension.dart';
import 'package:appflowy_editor/src/render/rich_text/flowy_rich_text_keys.dart';
import 'package:appflowy_editor/src/render/selection/selectable.dart';
import 'package:appflowy_editor/src/extensions/text_style_extension.dart';
import 'package:appflowy_editor/src/editor/util/color_util.dart';
import 'package:appflowy_editor/src/core/document/path.dart';
import 'package:flutter/gestures.dart';
import 'package:flutter/material.dart';
import 'package:flutter/rendering.dart';
import 'package:appflowy_editor/appflowy_editor.dart';
const _kRichTextDebugMode = false;
typedef FlowyTextSpanDecorator = TextSpan Function(TextSpan textSpan);
@ -376,3 +387,35 @@ class _FlowyRichTextState extends State<FlowyRichText> with SelectableMixin {
return textSelection;
}
}
extension FlowyRichTextAttributes on Attributes {
bool get bold => this[FlowyRichTextKeys.bold] == true;
bool get italic => this[FlowyRichTextKeys.italic] == true;
bool get underline => this[FlowyRichTextKeys.underline] == true;
bool get code => this[FlowyRichTextKeys.code] == true;
bool get strikethrough {
return (containsKey(FlowyRichTextKeys.strikethrough) &&
this[FlowyRichTextKeys.strikethrough] == true);
}
Color? get color {
final textColor = this[FlowyRichTextKeys.textColor] as String?;
return textColor?.toColor();
}
Color? get backgroundColor {
final highlightColor = this[FlowyRichTextKeys.highlightColor] as String?;
return highlightColor?.toColor();
}
String? get href {
if (this[FlowyRichTextKeys.href] is String) {
return this[FlowyRichTextKeys.href];
}
return null;
}
}

View file

@ -0,0 +1,10 @@
class FlowyRichTextKeys {
static String bold = 'bold';
static String italic = 'italic';
static String underline = 'underline';
static String strikethrough = 'strikethrough';
static String textColor = 'font_color';
static String highlightColor = 'bg_color';
static String code = 'code';
static String href = 'href';
}

View file

@ -227,7 +227,7 @@ ShortcutEventHandler markdownLinkOrImageHandler = (editorState, event) {
textNode.path,
Node.fromJson({
'type': 'image',
'attributes': {
'data': {
'image_src': imgUrl,
'align': 'center',
}

View file

@ -3,37 +3,37 @@ import 'package:appflowy_editor/appflowy_editor.dart';
const standardBlockComponentConfiguration = BlockComponentConfiguration();
final Map<String, BlockComponentBuilder> standardBlockComponentBuilderMap = {
'document': DocumentComponentBuilder(),
'paragraph': TextBlockComponentBuilder(
PageBlockKeys.type: PageBlockComponentBuilder(),
ParagraphBlockKeys.type: TextBlockComponentBuilder(
configuration: standardBlockComponentConfiguration,
),
'todo_list': TodoListBlockComponentBuilder(
TodoListBlockKeys.type: TodoListBlockComponentBuilder(
configuration: standardBlockComponentConfiguration.copyWith(
placeholderText: (_) => 'To-do',
),
),
'bulleted_list': BulletedListBlockComponentBuilder(
BulletedListBlockKeys.type: BulletedListBlockComponentBuilder(
configuration: standardBlockComponentConfiguration.copyWith(
placeholderText: (_) => 'List',
),
),
'numbered_list': NumberedListBlockComponentBuilder(
NumberedListBlockKeys.type: NumberedListBlockComponentBuilder(
configuration: standardBlockComponentConfiguration.copyWith(
placeholderText: (_) => 'List',
),
),
'quote': QuoteBlockComponentBuilder(
QuoteBlockKeys.type: QuoteBlockComponentBuilder(
configuration: standardBlockComponentConfiguration.copyWith(
placeholderText: (_) => 'Quote',
),
),
'heading': HeadingBlockComponentBuilder(
HeadingBlockKeys.type: HeadingBlockComponentBuilder(
configuration: standardBlockComponentConfiguration.copyWith(
placeholderText: (node) =>
'Heading ${node.attributes[HeadingBlockKeys.level]}',
),
),
'image': ImageBlockComponentBuilder(),
ImageBlockKeys.type: ImageBlockComponentBuilder(),
};
final List<CharacterShortcutEvent> standardCharacterShortcutEvents = [

View file

@ -66,7 +66,7 @@ void main() async {
'children': [
{'type': 'text'}
],
'attributes': {'a': 'a'}
'data': {'a': 'a'}
}
};
final document = Document.fromJson(json);
@ -78,11 +78,11 @@ void main() async {
true,
Document.fromJson({
'document': {
'type': 'document',
'type': 'page',
'children': [
{
'type': 'paragraph',
'attributes': {
'data': {
'delta': [],
}
}
@ -95,7 +95,7 @@ void main() async {
true,
Document.fromJson({
'document': {
'type': 'document',
'type': 'page',
'children': [],
}
}).isEmpty,
@ -105,11 +105,11 @@ void main() async {
true,
Document.fromJson({
'document': {
'type': 'document',
'type': 'page',
'children': [
{
'type': 'paragraph',
'attributes': {
'data': {
'delta': [
{'insert': ''}
],
@ -124,11 +124,11 @@ void main() async {
false,
Document.fromJson({
'document': {
'type': 'document',
'type': 'page',
'children': [
{
'type': 'paragraph',
'attributes': {
'data': {
'delta': [
{'insert': 'Welcome to AppFlowy!'}
],

View file

@ -12,7 +12,7 @@ void main() async {
);
expect(node.toJson(), {
'type': 'example',
'attributes': {
'data': {
'example': 'example',
},
});
@ -30,13 +30,13 @@ void main() async {
);
expect(nodeWithChildren.toJson(), {
'type': 'example',
'attributes': {
'data': {
'example': 'example',
},
'children': [
{
'type': 'example',
'attributes': {
'data': {
'example': 'example',
},
},
@ -57,7 +57,7 @@ void main() async {
);
expect(textNode.toJson(), {
'type': 'text',
'attributes': {
'data': {
'example': 'example',
},
'delta': [
@ -78,7 +78,7 @@ void main() async {
);
expect(textNodeWithChildren.toJson(), {
'type': 'text',
'attributes': {
'data': {
'example': 'example',
},
'delta': [
@ -87,7 +87,7 @@ void main() async {
'children': [
{
'type': 'text',
'attributes': {
'data': {
'example': 'example',
},
'delta': [
@ -185,7 +185,7 @@ void main() async {
'children': [
{
'type': 'example',
'attributes': {
'data': {
'example': 'example',
},
},

Some files were not shown because too many files have changed in this diff Show more