From 51bc965029f96c966f6d8c1cb84f68c86b486cd0 Mon Sep 17 00:00:00 2001 From: "Lucas.Xu" Date: Thu, 28 Jul 2022 19:59:15 +0800 Subject: [PATCH] feat: add bulleted-list and number-list --- .../flowy_editor/example/assets/example.json | 21 +++--- .../flowy_editor/lib/editor_state.dart | 6 ++ .../render/rich_text/bulleted_list_text.dart | 73 ++++++++++++++++++ .../lib/render/rich_text/flowy_rich_text.dart | 6 +- .../lib/render/rich_text/heading_text.dart | 3 - .../render/rich_text/number_list_text.dart | 74 +++++++++++++++++++ 6 files changed, 168 insertions(+), 15 deletions(-) create mode 100644 frontend/app_flowy/packages/flowy_editor/lib/render/rich_text/bulleted_list_text.dart create mode 100644 frontend/app_flowy/packages/flowy_editor/lib/render/rich_text/number_list_text.dart diff --git a/frontend/app_flowy/packages/flowy_editor/example/assets/example.json b/frontend/app_flowy/packages/flowy_editor/example/assets/example.json index d482ab2450..2fb4a4d8a0 100644 --- a/frontend/app_flowy/packages/flowy_editor/example/assets/example.json +++ b/frontend/app_flowy/packages/flowy_editor/example/assets/example.json @@ -111,8 +111,8 @@ { "insert": " and just typing." } ], "attributes": { - "list": "todo", - "todo": true + "subtype": "checkbox", + "checkbox": true } }, { @@ -130,8 +130,8 @@ } ], "attributes": { - "list": "todo", - "todo": true + "subtype": "checkbox", + "checkbox": true } }, { @@ -146,8 +146,8 @@ { "insert": "." } ], "attributes": { - "list": "todo", - "todo": true + "subtype": "checkbox", + "checkbox": true } }, { @@ -170,7 +170,7 @@ } ], "attributes": { - "list": "bullet" + "subtype": "bullet-list" } }, { @@ -181,7 +181,7 @@ } ], "attributes": { - "list": "bullet" + "subtype": "bullet-list" } }, { @@ -192,7 +192,7 @@ } ], "attributes": { - "list": "bullet" + "subtype": "bullet-list" } }, { @@ -225,6 +225,7 @@ } ], "attributes": { + "subtype": "number-list", "number": 1 } }, @@ -236,6 +237,7 @@ } ], "attributes": { + "subtype": "number-list", "number": 2 } }, @@ -247,6 +249,7 @@ } ], "attributes": { + "subtype": "number-list", "number": 3 } } diff --git a/frontend/app_flowy/packages/flowy_editor/lib/editor_state.dart b/frontend/app_flowy/packages/flowy_editor/lib/editor_state.dart index 6bc07078d3..a417d80513 100644 --- a/frontend/app_flowy/packages/flowy_editor/lib/editor_state.dart +++ b/frontend/app_flowy/packages/flowy_editor/lib/editor_state.dart @@ -1,7 +1,9 @@ import 'dart:async'; +import 'package:flowy_editor/render/rich_text/bulleted_list_text.dart'; import 'package:flowy_editor/render/rich_text/checkbox_text.dart'; import 'package:flowy_editor/render/rich_text/flowy_rich_text.dart'; import 'package:flowy_editor/render/rich_text/heading_text.dart'; +import 'package:flowy_editor/render/rich_text/number_list_text.dart'; import 'package:flowy_editor/service/service.dart'; import 'package:flutter/material.dart'; @@ -47,6 +49,10 @@ class EditorState { renderPlugins.register('text', RichTextNodeWidgetBuilder.create); renderPlugins.register('text/checkbox', CheckboxNodeWidgetBuilder.create); renderPlugins.register('text/heading', HeadingTextNodeWidgetBuilder.create); + renderPlugins.register( + 'text/bullet-list', BulletedListTextNodeWidgetBuilder.create); + renderPlugins.register( + 'text/number-list', NumberListTextNodeWidgetBuilder.create); undoManager.state = this; } diff --git a/frontend/app_flowy/packages/flowy_editor/lib/render/rich_text/bulleted_list_text.dart b/frontend/app_flowy/packages/flowy_editor/lib/render/rich_text/bulleted_list_text.dart new file mode 100644 index 0000000000..faf1cb8c56 --- /dev/null +++ b/frontend/app_flowy/packages/flowy_editor/lib/render/rich_text/bulleted_list_text.dart @@ -0,0 +1,73 @@ +import 'package:flowy_editor/document/node.dart'; +import 'package:flowy_editor/editor_state.dart'; +import 'package:flowy_editor/infra/flowy_svg.dart'; +import 'package:flowy_editor/render/node_widget_builder.dart'; +import 'package:flowy_editor/render/rich_text/default_selectable.dart'; +import 'package:flowy_editor/render/rich_text/flowy_rich_text.dart'; +import 'package:flowy_editor/render/selection/selectable.dart'; +import 'package:flutter/material.dart'; + +class BulletedListTextNodeWidgetBuilder extends NodeWidgetBuilder { + BulletedListTextNodeWidgetBuilder.create({ + required super.editorState, + required super.node, + required super.key, + }) : super.create(); + + @override + Widget build(BuildContext context) { + return BulletedListTextNodeWidget( + key: key, + textNode: node as TextNode, + editorState: editorState, + ); + } +} + +class BulletedListTextNodeWidget extends StatefulWidget { + const BulletedListTextNodeWidget({ + Key? key, + required this.textNode, + required this.editorState, + }) : super(key: key); + + final TextNode textNode; + final EditorState editorState; + + @override + State createState() => + _BulletedListTextNodeWidgetState(); +} + +// customize + +class _BulletedListTextNodeWidgetState extends State + with Selectable, DefaultSelectable { + final _richTextKey = GlobalKey(debugLabel: 'heading_text'); + final leftPadding = 20.0; + + @override + Selectable get forward => + _richTextKey.currentState as Selectable; + + @override + Offset get baseOffset { + return Offset(leftPadding, 0); + } + + @override + Widget build(BuildContext context) { + return Row( + children: [ + const FlowySvg( + name: 'point', + ), + FlowyRichText( + key: _richTextKey, + textNode: widget.textNode, + editorState: widget.editorState, + ), + ], + ); + } +} diff --git a/frontend/app_flowy/packages/flowy_editor/lib/render/rich_text/flowy_rich_text.dart b/frontend/app_flowy/packages/flowy_editor/lib/render/rich_text/flowy_rich_text.dart index 33b1ece074..0891ee72cf 100644 --- a/frontend/app_flowy/packages/flowy_editor/lib/render/rich_text/flowy_rich_text.dart +++ b/frontend/app_flowy/packages/flowy_editor/lib/render/rich_text/flowy_rich_text.dart @@ -68,15 +68,15 @@ class _FlowyRichTextState extends State with Selectable { final attributes = _textNode.attributes; // TODO: use factory method ?? if (attributes.list == 'todo') { - return _buildTodoListRichText(context); + // return _buildTodoListRichText(context); } else if (attributes.list == 'bullet') { - return _buildBulletedListRichText(context); + // return _buildBulletedListRichText(context); } else if (attributes.quote == true) { return _buildQuotedRichText(context); } else if (attributes.heading != null) { // return _buildHeadingRichText(context); } else if (attributes.number != null) { - return _buildNumberListRichText(context); + // return _buildNumberListRichText(context); } return _buildRichText(context); } diff --git a/frontend/app_flowy/packages/flowy_editor/lib/render/rich_text/heading_text.dart b/frontend/app_flowy/packages/flowy_editor/lib/render/rich_text/heading_text.dart index ac4746b3e4..3d2ac14756 100644 --- a/frontend/app_flowy/packages/flowy_editor/lib/render/rich_text/heading_text.dart +++ b/frontend/app_flowy/packages/flowy_editor/lib/render/rich_text/heading_text.dart @@ -1,13 +1,10 @@ import 'package:flowy_editor/document/node.dart'; import 'package:flowy_editor/editor_state.dart'; -import 'package:flowy_editor/infra/flowy_svg.dart'; import 'package:flowy_editor/render/node_widget_builder.dart'; -import 'package:flowy_editor/render/render_plugins.dart'; import 'package:flowy_editor/render/rich_text/default_selectable.dart'; import 'package:flowy_editor/render/rich_text/flowy_rich_text.dart'; import 'package:flowy_editor/render/rich_text/rich_text_style.dart'; import 'package:flowy_editor/render/selection/selectable.dart'; -import 'package:flowy_editor/extensions/object_extensions.dart'; import 'package:flutter/material.dart'; class HeadingTextNodeWidgetBuilder extends NodeWidgetBuilder { diff --git a/frontend/app_flowy/packages/flowy_editor/lib/render/rich_text/number_list_text.dart b/frontend/app_flowy/packages/flowy_editor/lib/render/rich_text/number_list_text.dart new file mode 100644 index 0000000000..082be930e9 --- /dev/null +++ b/frontend/app_flowy/packages/flowy_editor/lib/render/rich_text/number_list_text.dart @@ -0,0 +1,74 @@ +import 'package:flowy_editor/document/node.dart'; +import 'package:flowy_editor/editor_state.dart'; +import 'package:flowy_editor/infra/flowy_svg.dart'; +import 'package:flowy_editor/render/node_widget_builder.dart'; +import 'package:flowy_editor/render/rich_text/default_selectable.dart'; +import 'package:flowy_editor/render/rich_text/flowy_rich_text.dart'; +import 'package:flowy_editor/render/rich_text/rich_text_style.dart'; +import 'package:flowy_editor/render/selection/selectable.dart'; +import 'package:flutter/material.dart'; + +class NumberListTextNodeWidgetBuilder extends NodeWidgetBuilder { + NumberListTextNodeWidgetBuilder.create({ + required super.editorState, + required super.node, + required super.key, + }) : super.create(); + + @override + Widget build(BuildContext context) { + return NumberListTextNodeWidget( + key: key, + textNode: node as TextNode, + editorState: editorState, + ); + } +} + +class NumberListTextNodeWidget extends StatefulWidget { + const NumberListTextNodeWidget({ + Key? key, + required this.textNode, + required this.editorState, + }) : super(key: key); + + final TextNode textNode; + final EditorState editorState; + + @override + State createState() => + _NumberListTextNodeWidgetState(); +} + +// customize + +class _NumberListTextNodeWidgetState extends State + with Selectable, DefaultSelectable { + final _richTextKey = GlobalKey(debugLabel: 'heading_text'); + final leftPadding = 20.0; + + @override + Selectable get forward => + _richTextKey.currentState as Selectable; + + @override + Offset get baseOffset { + return Offset(leftPadding, 0); + } + + @override + Widget build(BuildContext context) { + return Row( + children: [ + FlowySvg( + number: widget.textNode.attributes.number, + ), + FlowyRichText( + key: _richTextKey, + textNode: widget.textNode, + editorState: widget.editorState, + ), + ], + ); + } +}